Salome HOME
bos #24194: EDF - SplitHexahedraIntoPrisms
[modules/smesh.git] / src / SMESH / SMESH_MeshEditor.cxx
1 // Copyright (C) 2007-2021  CEA/DEN, EDF R&D, OPEN CASCADE
2 //
3 // Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
5 //
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License, or (at your option) any later version.
10 //
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 // Lesser General Public License for more details.
15 //
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
19 //
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
21 //
22
23 // File      : SMESH_MeshEditor.cxx
24 // Created   : Mon Apr 12 16:10:22 2004
25 // Author    : Edward AGAPOV (eap)
26
27 #include "SMESH_MeshEditor.hxx"
28
29 #include "SMDS_Downward.hxx"
30 #include "SMDS_EdgePosition.hxx"
31 #include "SMDS_FaceOfNodes.hxx"
32 #include "SMDS_FacePosition.hxx"
33 #include "SMDS_LinearEdge.hxx"
34 #include "SMDS_MeshGroup.hxx"
35 #include "SMDS_SetIterator.hxx"
36 #include "SMDS_SpacePosition.hxx"
37 #include "SMDS_VolumeTool.hxx"
38 #include "SMESHDS_Group.hxx"
39 #include "SMESHDS_Mesh.hxx"
40 #include "SMESH_Algo.hxx"
41 #include "SMESH_ControlsDef.hxx"
42 #include "SMESH_Group.hxx"
43 #include "SMESH_Mesh.hxx"
44 #include "SMESH_MeshAlgos.hxx"
45 #include "SMESH_MesherHelper.hxx"
46 #include "SMESH_OctreeNode.hxx"
47 #include "SMESH_subMesh.hxx"
48
49 #include "utilities.h"
50 #include "chrono.hxx"
51
52 #include <BRepAdaptor_Surface.hxx>
53 #include <BRepBuilderAPI_MakeEdge.hxx>
54 #include <BRepClass3d_SolidClassifier.hxx>
55 #include <BRep_Tool.hxx>
56 #include <ElCLib.hxx>
57 #include <Extrema_GenExtPS.hxx>
58 #include <Extrema_POnCurv.hxx>
59 #include <Extrema_POnSurf.hxx>
60 #include <Geom2d_Curve.hxx>
61 #include <GeomAdaptor_Surface.hxx>
62 #include <Geom_Curve.hxx>
63 #include <Geom_Surface.hxx>
64 #include <Precision.hxx>
65 #include <TColStd_ListOfInteger.hxx>
66 #include <TopAbs_State.hxx>
67 #include <TopExp.hxx>
68 #include <TopExp_Explorer.hxx>
69 #include <TopTools_ListIteratorOfListOfShape.hxx>
70 #include <TopTools_ListOfShape.hxx>
71 #include <TopTools_SequenceOfShape.hxx>
72 #include <TopoDS.hxx>
73 #include <TopoDS_Edge.hxx>
74 #include <TopoDS_Face.hxx>
75 #include <TopoDS_Solid.hxx>
76 #include <gp.hxx>
77 #include <gp_Ax1.hxx>
78 #include <gp_Dir.hxx>
79 #include <gp_Lin.hxx>
80 #include <gp_Pln.hxx>
81 #include <gp_Trsf.hxx>
82 #include <gp_Vec.hxx>
83 #include <gp_XY.hxx>
84 #include <gp_XYZ.hxx>
85
86 #include <cmath>
87
88 #include <map>
89 #include <set>
90 #include <numeric>
91 #include <limits>
92 #include <algorithm>
93 #include <sstream>
94
95 #include <boost/tuple/tuple.hpp>
96 #include <boost/container/flat_set.hpp>
97
98 #include <Standard_Failure.hxx>
99 #include <Standard_ErrorHandler.hxx>
100
101 #include "SMESH_TryCatch.hxx" // include after OCCT headers!
102
103 #define cast2Node(elem) static_cast<const SMDS_MeshNode*>( elem )
104
105 using namespace std;
106 using namespace SMESH::Controls;
107
108 //=======================================================================
109 //function : SMESH_MeshEditor
110 //purpose  :
111 //=======================================================================
112
113 SMESH_MeshEditor::SMESH_MeshEditor( SMESH_Mesh* theMesh )
114   :myMesh( theMesh ) // theMesh may be NULL
115 {
116 }
117
118 //================================================================================
119 /*!
120  * \brief Return mesh DS
121  */
122 //================================================================================
123
124 SMESHDS_Mesh * SMESH_MeshEditor::GetMeshDS()
125 {
126   return myMesh->GetMeshDS();
127 }
128
129
130 //================================================================================
131 /*!
132  * \brief Clears myLastCreatedNodes and myLastCreatedElems
133  */
134 //================================================================================
135
136 void SMESH_MeshEditor::ClearLastCreated()
137 {
138   SMESHUtils::FreeVector( myLastCreatedElems );
139   SMESHUtils::FreeVector( myLastCreatedNodes );
140 }
141
142 //================================================================================
143 /*!
144  * \brief Initializes members by an existing element
145  *  \param [in] elem - the source element
146  *  \param [in] basicOnly - if true, does not set additional data of Ball and Polyhedron
147  */
148 //================================================================================
149
150 SMESH_MeshEditor::ElemFeatures&
151 SMESH_MeshEditor::ElemFeatures::Init( const SMDS_MeshElement* elem, bool basicOnly )
152 {
153   if ( elem )
154   {
155     myType = elem->GetType();
156     if ( myType == SMDSAbs_Face || myType == SMDSAbs_Volume )
157     {
158       myIsPoly = elem->IsPoly();
159       if ( myIsPoly )
160       {
161         myIsQuad = elem->IsQuadratic();
162         if ( myType == SMDSAbs_Volume && !basicOnly )
163         {
164           myPolyhedQuantities = static_cast<const SMDS_MeshVolume* >( elem )->GetQuantities();
165         }
166       }
167     }
168     else if ( myType == SMDSAbs_Ball && !basicOnly )
169     {
170       myBallDiameter = static_cast<const SMDS_BallElement*>(elem)->GetDiameter();
171     }
172   }
173   return *this;
174 }
175
176 //=======================================================================
177 /*!
178  * \brief Add element
179  */
180 //=======================================================================
181
182 SMDS_MeshElement*
183 SMESH_MeshEditor::AddElement(const vector<const SMDS_MeshNode*> & node,
184                              const ElemFeatures&                  features)
185 {
186   SMDS_MeshElement* e = 0;
187   int nbnode = node.size();
188   SMESHDS_Mesh* mesh = GetMeshDS();
189   const int ID = features.myID;
190
191   switch ( features.myType ) {
192   case SMDSAbs_Face:
193     if ( !features.myIsPoly ) {
194       if      (nbnode == 3) {
195         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], ID);
196         else           e = mesh->AddFace      (node[0], node[1], node[2] );
197       }
198       else if (nbnode == 4) {
199         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], ID);
200         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3] );
201       }
202       else if (nbnode == 6) {
203         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
204                                                node[4], node[5], ID);
205         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
206                                                node[4], node[5] );
207       }
208       else if (nbnode == 7) {
209         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
210                                                node[4], node[5], node[6], ID);
211         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
212                                                node[4], node[5], node[6] );
213       }
214       else if (nbnode == 8) {
215         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
216                                                node[4], node[5], node[6], node[7], ID);
217         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
218                                                node[4], node[5], node[6], node[7] );
219       }
220       else if (nbnode == 9) {
221         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
222                                                node[4], node[5], node[6], node[7], node[8], ID);
223         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
224                                                node[4], node[5], node[6], node[7], node[8] );
225       }
226     }
227     else if ( !features.myIsQuad )
228     {
229       if ( ID >= 1 ) e = mesh->AddPolygonalFaceWithID(node, ID);
230       else           e = mesh->AddPolygonalFace      (node    );
231     }
232     else if ( nbnode % 2 == 0 ) // just a protection
233     {
234       if ( ID >= 1 ) e = mesh->AddQuadPolygonalFaceWithID(node, ID);
235       else           e = mesh->AddQuadPolygonalFace      (node    );
236     }
237     break;
238
239   case SMDSAbs_Volume:
240     if ( !features.myIsPoly ) {
241       if      (nbnode == 4) {
242         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], ID);
243         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3] );
244       }
245       else if (nbnode == 5) {
246         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
247                                                  node[4], ID);
248         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
249                                                  node[4] );
250       }
251       else if (nbnode == 6) {
252         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
253                                                  node[4], node[5], ID);
254         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
255                                                  node[4], node[5] );
256       }
257       else if (nbnode == 8) {
258         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
259                                                  node[4], node[5], node[6], node[7], ID);
260         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
261                                                  node[4], node[5], node[6], node[7] );
262       }
263       else if (nbnode == 10) {
264         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
265                                                  node[4], node[5], node[6], node[7],
266                                                  node[8], node[9], ID);
267         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
268                                                  node[4], node[5], node[6], node[7],
269                                                  node[8], node[9] );
270       }
271       else if (nbnode == 12) {
272         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
273                                                  node[4], node[5], node[6], node[7],
274                                                  node[8], node[9], node[10], node[11], ID);
275         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
276                                                  node[4], node[5], node[6], node[7],
277                                                  node[8], node[9], node[10], node[11] );
278       }
279       else if (nbnode == 13) {
280         if ( ID >= 1 ) e = mesh->AddVolumeWithID(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                                                  node[12],ID);
284         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
285                                                  node[4], node[5], node[6], node[7],
286                                                  node[8], node[9], node[10],node[11],
287                                                  node[12] );
288       }
289       else if (nbnode == 15) {
290         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
291                                                  node[4], node[5], node[6], node[7],
292                                                  node[8], node[9], node[10],node[11],
293                                                  node[12],node[13],node[14],ID);
294         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
295                                                  node[4], node[5], node[6], node[7],
296                                                  node[8], node[9], node[10],node[11],
297                                                  node[12],node[13],node[14] );
298       }
299       else if (nbnode == 18) {
300         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
301                                                  node[4], node[5], node[6], node[7],
302                                                  node[8], node[9], node[10],node[11],
303                                                  node[12],node[13],node[14],
304                                                  node[15],node[16],node[17],ID );
305         else           e = mesh->AddVolume      (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] );
310       }
311       else if (nbnode == 20) {
312         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
313                                                  node[4], node[5], node[6], node[7],
314                                                  node[8], node[9], node[10],node[11],
315                                                  node[12],node[13],node[14],node[15],
316                                                  node[16],node[17],node[18],node[19],ID);
317         else           e = mesh->AddVolume      (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] );
322       }
323       else if (nbnode == 27) {
324         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
325                                                  node[4], node[5], node[6], node[7],
326                                                  node[8], node[9], node[10],node[11],
327                                                  node[12],node[13],node[14],node[15],
328                                                  node[16],node[17],node[18],node[19],
329                                                  node[20],node[21],node[22],node[23],
330                                                  node[24],node[25],node[26], ID);
331         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
332                                                  node[4], node[5], node[6], node[7],
333                                                  node[8], node[9], node[10],node[11],
334                                                  node[12],node[13],node[14],node[15],
335                                                  node[16],node[17],node[18],node[19],
336                                                  node[20],node[21],node[22],node[23],
337                                                  node[24],node[25],node[26] );
338       }
339     }
340     else if ( !features.myIsQuad )
341     {
342       if ( ID >= 1 ) e = mesh->AddPolyhedralVolumeWithID(node, features.myPolyhedQuantities, ID);
343       else           e = mesh->AddPolyhedralVolume      (node, features.myPolyhedQuantities    );
344     }
345     else
346     {
347       // if ( ID >= 1 ) e = mesh->AddQuadPolyhedralVolumeWithID(node, features.myPolyhedQuantities,ID);
348       // else           e = mesh->AddQuadPolyhedralVolume      (node, features.myPolyhedQuantities   );
349     }
350     break;
351
352   case SMDSAbs_Edge:
353     if ( nbnode == 2 ) {
354       if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], ID);
355       else           e = mesh->AddEdge      (node[0], node[1] );
356     }
357     else if ( nbnode == 3 ) {
358       if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], node[2], ID);
359       else           e = mesh->AddEdge      (node[0], node[1], node[2] );
360     }
361     break;
362
363   case SMDSAbs_0DElement:
364     if ( nbnode == 1 ) {
365       if ( ID >= 1 ) e = mesh->Add0DElementWithID(node[0], ID);
366       else           e = mesh->Add0DElement      (node[0] );
367     }
368     break;
369
370   case SMDSAbs_Node:
371     if ( ID >= 1 ) e = mesh->AddNodeWithID(node[0]->X(), node[0]->Y(), node[0]->Z(), ID);
372     else           e = mesh->AddNode      (node[0]->X(), node[0]->Y(), node[0]->Z()    );
373     break;
374
375   case SMDSAbs_Ball:
376     if ( ID >= 1 ) e = mesh->AddBallWithID(node[0], features.myBallDiameter, ID);
377     else           e = mesh->AddBall      (node[0], features.myBallDiameter    );
378     break;
379
380   default:;
381   }
382   if ( e ) myLastCreatedElems.push_back( e );
383   return e;
384 }
385
386 //=======================================================================
387 /*!
388  * \brief Add element
389  */
390 //=======================================================================
391
392 SMDS_MeshElement* SMESH_MeshEditor::AddElement(const vector<int> & nodeIDs,
393                                                const ElemFeatures& features)
394 {
395   vector<const SMDS_MeshNode*> nodes;
396   nodes.reserve( nodeIDs.size() );
397   vector<int>::const_iterator id = nodeIDs.begin();
398   while ( id != nodeIDs.end() ) {
399     if ( const SMDS_MeshNode* node = GetMeshDS()->FindNode( *id++ ))
400       nodes.push_back( node );
401     else
402       return 0;
403   }
404   return AddElement( nodes, features );
405 }
406
407 //=======================================================================
408 //function : Remove
409 //purpose  : Remove a node or an element.
410 //           Modify a compute state of sub-meshes which become empty
411 //=======================================================================
412
413 int SMESH_MeshEditor::Remove (const list< int >& theIDs,
414                               const bool         isNodes )
415 {
416   ClearLastCreated();
417
418   SMESHDS_Mesh* aMesh = GetMeshDS();
419   set< SMESH_subMesh *> smmap;
420
421   int removed = 0;
422   list<int>::const_iterator it = theIDs.begin();
423   for ( ; it != theIDs.end(); it++ ) {
424     const SMDS_MeshElement * elem;
425     if ( isNodes )
426       elem = aMesh->FindNode( *it );
427     else
428       elem = aMesh->FindElement( *it );
429     if ( !elem )
430       continue;
431
432     // Notify VERTEX sub-meshes about modification
433     if ( isNodes ) {
434       const SMDS_MeshNode* node = cast2Node( elem );
435       if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX )
436         if ( int aShapeID = node->getshapeId() )
437           if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
438             smmap.insert( sm );
439     }
440     // Find sub-meshes to notify about modification
441     //     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
442     //     while ( nodeIt->more() ) {
443     //       const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
444     //       const SMDS_PositionPtr& aPosition = node->GetPosition();
445     //       if ( aPosition.get() ) {
446     //         if ( int aShapeID = aPosition->GetShapeId() ) {
447     //           if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
448     //             smmap.insert( sm );
449     //         }
450     //       }
451     //     }
452
453     // Do remove
454     if ( isNodes )
455       aMesh->RemoveNode( static_cast< const SMDS_MeshNode* >( elem ));
456     else
457       aMesh->RemoveElement( elem );
458     removed++;
459   }
460
461   // Notify sub-meshes about modification
462   if ( !smmap.empty() ) {
463     set< SMESH_subMesh *>::iterator smIt;
464     for ( smIt = smmap.begin(); smIt != smmap.end(); smIt++ )
465       (*smIt)->ComputeStateEngine( SMESH_subMesh::MESH_ENTITY_REMOVED );
466   }
467
468   //   // Check if the whole mesh becomes empty
469   //   if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( 1 ) )
470   //     sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
471
472   return removed;
473 }
474
475 //================================================================================
476 /*!
477  * \brief Create 0D elements on all nodes of the given object.
478  *  \param elements - Elements on whose nodes to create 0D elements; if empty, 
479  *                    the all mesh is treated
480  *  \param all0DElems - returns all 0D elements found or created on nodes of \a elements
481  *  \param duplicateElements - to add one more 0D element to a node or not
482  */
483 //================================================================================
484
485 void SMESH_MeshEditor::Create0DElementsOnAllNodes( const TIDSortedElemSet& elements,
486                                                    TIDSortedElemSet&       all0DElems,
487                                                    const bool              duplicateElements )
488 {
489   SMDS_ElemIteratorPtr elemIt;
490   if ( elements.empty() )
491   {
492     elemIt = GetMeshDS()->elementsIterator( SMDSAbs_Node );
493   }
494   else
495   {
496     elemIt = SMESHUtils::elemSetIterator( elements );
497   }
498
499   while ( elemIt->more() )
500   {
501     const SMDS_MeshElement* e = elemIt->next();
502     SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
503     while ( nodeIt->more() )
504     {
505       const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
506       SMDS_ElemIteratorPtr it0D = n->GetInverseElementIterator( SMDSAbs_0DElement );
507       if ( duplicateElements || !it0D->more() )
508       {
509         myLastCreatedElems.push_back( GetMeshDS()->Add0DElement( n ));
510         all0DElems.insert( myLastCreatedElems.back() );
511       }
512       while ( it0D->more() )
513         all0DElems.insert( it0D->next() );
514     }
515   }
516 }
517
518 //=======================================================================
519 //function : FindShape
520 //purpose  : Return an index of the shape theElem is on
521 //           or zero if a shape not found
522 //=======================================================================
523
524 int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem)
525 {
526   ClearLastCreated();
527
528   SMESHDS_Mesh * aMesh = GetMeshDS();
529   if ( aMesh->ShapeToMesh().IsNull() )
530     return 0;
531
532   int aShapeID = theElem->getshapeId();
533   if ( aShapeID < 1 )
534     return 0;
535
536   if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ))
537     if ( sm->Contains( theElem ))
538       return aShapeID;
539
540   if ( theElem->GetType() == SMDSAbs_Node ) {
541     MESSAGE( ":( Error: invalid myShapeId of node " << theElem->GetID() );
542   }
543   else {
544     MESSAGE( ":( Error: invalid myShapeId of element " << theElem->GetID() );
545   }
546
547   TopoDS_Shape aShape; // the shape a node of theElem is on
548   if ( theElem->GetType() != SMDSAbs_Node )
549   {
550     SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
551     while ( nodeIt->more() ) {
552       const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
553       if ((aShapeID = node->getshapeId()) > 0) {
554         if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ) ) {
555           if ( sm->Contains( theElem ))
556             return aShapeID;
557           if ( aShape.IsNull() )
558             aShape = aMesh->IndexToShape( aShapeID );
559         }
560       }
561     }
562   }
563
564   // None of nodes is on a proper shape,
565   // find the shape among ancestors of aShape on which a node is
566   if ( !aShape.IsNull() ) {
567     TopTools_ListIteratorOfListOfShape ancIt( GetMesh()->GetAncestors( aShape ));
568     for ( ; ancIt.More(); ancIt.Next() ) {
569       SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() );
570       if ( sm && sm->Contains( theElem ))
571         return aMesh->ShapeToIndex( ancIt.Value() );
572     }
573   }
574   else
575   {
576     SMESHDS_SubMeshIteratorPtr smIt = GetMeshDS()->SubMeshes();
577     while ( const SMESHDS_SubMesh* sm = smIt->next() )
578       if ( sm->Contains( theElem ))
579         return sm->GetID();
580   }
581
582   return 0;
583 }
584
585 //=======================================================================
586 //function : IsMedium
587 //purpose  :
588 //=======================================================================
589
590 bool SMESH_MeshEditor::IsMedium(const SMDS_MeshNode*      node,
591                                 const SMDSAbs_ElementType typeToCheck)
592 {
593   bool isMedium = false;
594   SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(typeToCheck);
595   while (it->more() && !isMedium ) {
596     const SMDS_MeshElement* elem = it->next();
597     isMedium = elem->IsMediumNode(node);
598   }
599   return isMedium;
600 }
601
602 //=======================================================================
603 //function : shiftNodesQuadTria
604 //purpose  : Shift nodes in the array corresponded to quadratic triangle
605 //           example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
606 //=======================================================================
607
608 static void shiftNodesQuadTria(vector< const SMDS_MeshNode* >& aNodes)
609 {
610   const SMDS_MeshNode* nd1 = aNodes[0];
611   aNodes[0] = aNodes[1];
612   aNodes[1] = aNodes[2];
613   aNodes[2] = nd1;
614   const SMDS_MeshNode* nd2 = aNodes[3];
615   aNodes[3] = aNodes[4];
616   aNodes[4] = aNodes[5];
617   aNodes[5] = nd2;
618 }
619
620 //=======================================================================
621 //function : getNodesFromTwoTria
622 //purpose  : 
623 //=======================================================================
624
625 static bool getNodesFromTwoTria(const SMDS_MeshElement * theTria1,
626                                 const SMDS_MeshElement * theTria2,
627                                 vector< const SMDS_MeshNode*>& N1,
628                                 vector< const SMDS_MeshNode*>& N2)
629 {
630   N1.assign( theTria1->begin_nodes(), theTria1->end_nodes() );
631   if ( N1.size() < 6 ) return false;
632   N2.assign( theTria2->begin_nodes(), theTria2->end_nodes() );
633   if ( N2.size() < 6 ) return false;
634
635   int sames[3] = {-1,-1,-1};
636   int nbsames = 0;
637   int i, j;
638   for(i=0; i<3; i++) {
639     for(j=0; j<3; j++) {
640       if(N1[i]==N2[j]) {
641         sames[i] = j;
642         nbsames++;
643         break;
644       }
645     }
646   }
647   if(nbsames!=2) return false;
648   if(sames[0]>-1) {
649     shiftNodesQuadTria(N1);
650     if(sames[1]>-1) {
651       shiftNodesQuadTria(N1);
652     }
653   }
654   i = sames[0] + sames[1] + sames[2];
655   for(; i<2; i++) {
656     shiftNodesQuadTria(N2);
657   }
658   // now we receive following N1 and N2 (using numeration as in the image below)
659   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
660   // i.e. first nodes from both arrays form a new diagonal
661   return true;
662 }
663
664 //=======================================================================
665 //function : InverseDiag
666 //purpose  : Replace two neighbour triangles with ones built on the same 4 nodes
667 //           but having other common link.
668 //           Return False if args are improper
669 //=======================================================================
670
671 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1,
672                                     const SMDS_MeshElement * theTria2 )
673 {
674   ClearLastCreated();
675
676   if ( !theTria1 || !theTria2 ||
677        !dynamic_cast<const SMDS_MeshCell*>( theTria1 ) ||
678        !dynamic_cast<const SMDS_MeshCell*>( theTria2 ) ||
679        theTria1->GetType() != SMDSAbs_Face ||
680        theTria2->GetType() != SMDSAbs_Face )
681     return false;
682
683   if ((theTria1->GetEntityType() == SMDSEntity_Triangle) &&
684       (theTria2->GetEntityType() == SMDSEntity_Triangle))
685   {
686     //  1 +--+ A  theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
687     //    | /|    theTria2: ( B A 2 ) B->1 ( 1 A 2 )   |\ |
688     //    |/ |                                         | \|
689     //  B +--+ 2                                     B +--+ 2
690
691     // put nodes in array and find out indices of the same ones
692     const SMDS_MeshNode* aNodes [6];
693     int sameInd [] = { -1, -1, -1, -1, -1, -1 };
694     int i = 0;
695     SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
696     while ( it->more() ) {
697       aNodes[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
698
699       if ( i > 2 ) // theTria2
700         // find same node of theTria1
701         for ( int j = 0; j < 3; j++ )
702           if ( aNodes[ i ] == aNodes[ j ]) {
703             sameInd[ j ] = i;
704             sameInd[ i ] = j;
705             break;
706           }
707       // next
708       i++;
709       if ( i == 3 ) {
710         if ( it->more() )
711           return false; // theTria1 is not a triangle
712         it = theTria2->nodesIterator();
713       }
714       if ( i == 6 && it->more() )
715         return false; // theTria2 is not a triangle
716     }
717
718     // find indices of 1,2 and of A,B in theTria1
719     int iA = -1, iB = 0, i1 = 0, i2 = 0;
720     for ( i = 0; i < 6; i++ ) {
721       if ( sameInd [ i ] == -1 ) {
722         if ( i < 3 ) i1 = i;
723         else         i2 = i;
724       }
725       else if (i < 3) {
726         if ( iA >= 0) iB = i;
727         else          iA = i;
728       }
729     }
730     // nodes 1 and 2 should not be the same
731     if ( aNodes[ i1 ] == aNodes[ i2 ] )
732       return false;
733
734     // theTria1: A->2
735     aNodes[ iA ] = aNodes[ i2 ];
736     // theTria2: B->1
737     aNodes[ sameInd[ iB ]] = aNodes[ i1 ];
738
739     GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 );
740     GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 );
741
742     return true;
743
744   } // end if(F1 && F2)
745
746   // check case of quadratic faces
747   if (theTria1->GetEntityType() != SMDSEntity_Quad_Triangle &&
748       theTria1->GetEntityType() != SMDSEntity_BiQuad_Triangle)
749     return false;
750   if (theTria2->GetEntityType() != SMDSEntity_Quad_Triangle&&
751       theTria2->GetEntityType() != SMDSEntity_BiQuad_Triangle)
752     return false;
753
754   //       5
755   //  1 +--+--+ 2  theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
756   //    |    /|    theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
757   //    |   / |
758   //  7 +  +  + 6
759   //    | /9  |
760   //    |/    |
761   //  4 +--+--+ 3
762   //       8
763
764   vector< const SMDS_MeshNode* > N1;
765   vector< const SMDS_MeshNode* > N2;
766   if(!getNodesFromTwoTria(theTria1,theTria2,N1,N2))
767     return false;
768   // now we receive following N1 and N2 (using numeration as above image)
769   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
770   // i.e. first nodes from both arrays determ new diagonal
771
772   vector< const SMDS_MeshNode*> N1new( N1.size() );
773   vector< const SMDS_MeshNode*> N2new( N2.size() );
774   N1new.back() = N1.back(); // central node of biquadratic
775   N2new.back() = N2.back();
776   N1new[0] = N1[0];  N2new[0] = N1[0];
777   N1new[1] = N2[0];  N2new[1] = N1[1];
778   N1new[2] = N2[1];  N2new[2] = N2[0];
779   N1new[3] = N1[4];  N2new[3] = N1[3];
780   N1new[4] = N2[3];  N2new[4] = N2[5];
781   N1new[5] = N1[5];  N2new[5] = N1[4];
782   // change nodes in faces
783   GetMeshDS()->ChangeElementNodes( theTria1, &N1new[0], N1new.size() );
784   GetMeshDS()->ChangeElementNodes( theTria2, &N2new[0], N2new.size() );
785
786   // move the central node of biquadratic triangle
787   SMESH_MesherHelper helper( *GetMesh() );
788   for ( int is2nd = 0; is2nd < 2; ++is2nd )
789   {
790     const SMDS_MeshElement*         tria = is2nd ? theTria2 : theTria1;
791     vector< const SMDS_MeshNode*>& nodes = is2nd ? N2new : N1new;
792     if ( nodes.size() < 7 )
793       continue;
794     helper.SetSubShape( tria->getshapeId() );
795     const TopoDS_Face& F = TopoDS::Face( helper.GetSubShape() );
796     gp_Pnt xyz;
797     if ( F.IsNull() )
798     {
799       xyz = ( SMESH_NodeXYZ( nodes[3] ) +
800               SMESH_NodeXYZ( nodes[4] ) +
801               SMESH_NodeXYZ( nodes[5] )) / 3.;
802     }
803     else
804     {
805       bool checkUV;
806       gp_XY uv = ( helper.GetNodeUV( F, nodes[3], nodes[2], &checkUV ) +
807                    helper.GetNodeUV( F, nodes[4], nodes[0], &checkUV ) +
808                    helper.GetNodeUV( F, nodes[5], nodes[1], &checkUV )) / 3.;
809       TopLoc_Location loc;
810       Handle(Geom_Surface) S = BRep_Tool::Surface(F,loc);
811       xyz = S->Value( uv.X(), uv.Y() );
812       xyz.Transform( loc );
813       if ( nodes[6]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE &&  // set UV
814            nodes[6]->getshapeId() > 0 )
815         GetMeshDS()->SetNodeOnFace( nodes[6], nodes[6]->getshapeId(), uv.X(), uv.Y() );
816     }
817     GetMeshDS()->MoveNode( nodes[6], xyz.X(), xyz.Y(), xyz.Z() );
818   }
819   return true;
820 }
821
822 //=======================================================================
823 //function : findTriangles
824 //purpose  : find triangles sharing theNode1-theNode2 link
825 //=======================================================================
826
827 static bool findTriangles(const SMDS_MeshNode *    theNode1,
828                           const SMDS_MeshNode *    theNode2,
829                           const SMDS_MeshElement*& theTria1,
830                           const SMDS_MeshElement*& theTria2)
831 {
832   if ( !theNode1 || !theNode2 ) return false;
833
834   theTria1 = theTria2 = 0;
835
836   set< const SMDS_MeshElement* > emap;
837   SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face);
838   while (it->more()) {
839     const SMDS_MeshElement* elem = it->next();
840     if ( elem->NbCornerNodes() == 3 )
841       emap.insert( elem );
842   }
843   it = theNode2->GetInverseElementIterator(SMDSAbs_Face);
844   while (it->more()) {
845     const SMDS_MeshElement* elem = it->next();
846     if ( emap.count( elem )) {
847       if ( !theTria1 )
848       {
849         theTria1 = elem;
850       }
851       else  
852       {
853         theTria2 = elem;
854         // theTria1 must be element with minimum ID
855         if ( theTria2->GetID() < theTria1->GetID() )
856           std::swap( theTria2, theTria1 );
857         return true;
858       }
859     }
860   }
861   return false;
862 }
863
864 //=======================================================================
865 //function : InverseDiag
866 //purpose  : Replace two neighbour triangles sharing theNode1-theNode2 link
867 //           with ones built on the same 4 nodes but having other common link.
868 //           Return false if proper faces not found
869 //=======================================================================
870
871 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
872                                     const SMDS_MeshNode * theNode2)
873 {
874   ClearLastCreated();
875
876   const SMDS_MeshElement *tr1, *tr2;
877   if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
878     return false;
879
880   if ( !dynamic_cast<const SMDS_MeshCell*>( tr1 ) ||
881        !dynamic_cast<const SMDS_MeshCell*>( tr2 ))
882     return false;
883
884   if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
885       (tr2->GetEntityType() == SMDSEntity_Triangle)) {
886
887     //  1 +--+ A  tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
888     //    | /|    tr2: ( B A 2 ) B->1 ( 1 A 2 )   |\ |
889     //    |/ |                                    | \|
890     //  B +--+ 2                                B +--+ 2
891
892     // put nodes in array
893     // and find indices of 1,2 and of A in tr1 and of B in tr2
894     int i, iA1 = 0, i1 = 0;
895     const SMDS_MeshNode* aNodes1 [3];
896     SMDS_ElemIteratorPtr it;
897     for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) {
898       aNodes1[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
899       if ( aNodes1[ i ] == theNode1 )
900         iA1 = i; // node A in tr1
901       else if ( aNodes1[ i ] != theNode2 )
902         i1 = i;  // node 1
903     }
904     int iB2 = 0, i2 = 0;
905     const SMDS_MeshNode* aNodes2 [3];
906     for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) {
907       aNodes2[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
908       if ( aNodes2[ i ] == theNode2 )
909         iB2 = i; // node B in tr2
910       else if ( aNodes2[ i ] != theNode1 )
911         i2 = i;  // node 2
912     }
913
914     // nodes 1 and 2 should not be the same
915     if ( aNodes1[ i1 ] == aNodes2[ i2 ] )
916       return false;
917
918     // tr1: A->2
919     aNodes1[ iA1 ] = aNodes2[ i2 ];
920     // tr2: B->1
921     aNodes2[ iB2 ] = aNodes1[ i1 ];
922
923     GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 );
924     GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 );
925
926     return true;
927   }
928
929   // check case of quadratic faces
930   return InverseDiag(tr1,tr2);
931 }
932
933 //=======================================================================
934 //function : getQuadrangleNodes
935 //purpose  : fill theQuadNodes - nodes of a quadrangle resulting from
936 //           fusion of triangles tr1 and tr2 having shared link on
937 //           theNode1 and theNode2
938 //=======================================================================
939
940 bool getQuadrangleNodes(const SMDS_MeshNode *    theQuadNodes [],
941                         const SMDS_MeshNode *    theNode1,
942                         const SMDS_MeshNode *    theNode2,
943                         const SMDS_MeshElement * tr1,
944                         const SMDS_MeshElement * tr2 )
945 {
946   if( tr1->NbNodes() != tr2->NbNodes() )
947     return false;
948   // find the 4-th node to insert into tr1
949   const SMDS_MeshNode* n4 = 0;
950   SMDS_ElemIteratorPtr it = tr2->nodesIterator();
951   int i=0;
952   while ( !n4 && i<3 ) {
953     const SMDS_MeshNode * n = cast2Node( it->next() );
954     i++;
955     bool isDiag = ( n == theNode1 || n == theNode2 );
956     if ( !isDiag )
957       n4 = n;
958   }
959   // Make an array of nodes to be in a quadrangle
960   int iNode = 0, iFirstDiag = -1;
961   it = tr1->nodesIterator();
962   i=0;
963   while ( i<3 ) {
964     const SMDS_MeshNode * n = cast2Node( it->next() );
965     i++;
966     bool isDiag = ( n == theNode1 || n == theNode2 );
967     if ( isDiag ) {
968       if ( iFirstDiag < 0 )
969         iFirstDiag = iNode;
970       else if ( iNode - iFirstDiag == 1 )
971         theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes
972     }
973     else if ( n == n4 ) {
974       return false; // tr1 and tr2 should not have all the same nodes
975     }
976     theQuadNodes[ iNode++ ] = n;
977   }
978   if ( iNode == 3 ) // diagonal nodes have 0 and 2 indices
979     theQuadNodes[ iNode ] = n4;
980
981   return true;
982 }
983
984 //=======================================================================
985 //function : DeleteDiag
986 //purpose  : Replace two neighbour triangles sharing theNode1-theNode2 link
987 //           with a quadrangle built on the same 4 nodes.
988 //           Return false if proper faces not found
989 //=======================================================================
990
991 bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
992                                    const SMDS_MeshNode * theNode2)
993 {
994   ClearLastCreated();
995
996   const SMDS_MeshElement *tr1, *tr2;
997   if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
998     return false;
999
1000   if ( !dynamic_cast<const SMDS_MeshCell*>( tr1 ) ||
1001        !dynamic_cast<const SMDS_MeshCell*>( tr2 ))
1002     return false;
1003
1004   SMESHDS_Mesh * aMesh = GetMeshDS();
1005
1006   if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
1007       (tr2->GetEntityType() == SMDSEntity_Triangle))
1008   {
1009     const SMDS_MeshNode* aNodes [ 4 ];
1010     if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 ))
1011       return false;
1012
1013     const SMDS_MeshElement* newElem = 0;
1014     newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3] );
1015     myLastCreatedElems.push_back(newElem);
1016     AddToSameGroups( newElem, tr1, aMesh );
1017     int aShapeId = tr1->getshapeId();
1018     if ( aShapeId )
1019       aMesh->SetMeshElementOnShape( newElem, aShapeId );
1020
1021     aMesh->RemoveElement( tr1 );
1022     aMesh->RemoveElement( tr2 );
1023
1024     return true;
1025   }
1026
1027   // check case of quadratic faces
1028   if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
1029     return false;
1030   if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
1031     return false;
1032
1033   //       5
1034   //  1 +--+--+ 2  tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
1035   //    |    /|    tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
1036   //    |   / |
1037   //  7 +  +  + 6
1038   //    | /9  |
1039   //    |/    |
1040   //  4 +--+--+ 3
1041   //       8
1042
1043   vector< const SMDS_MeshNode* > N1;
1044   vector< const SMDS_MeshNode* > N2;
1045   if(!getNodesFromTwoTria(tr1,tr2,N1,N2))
1046     return false;
1047   // now we receive following N1 and N2 (using numeration as above image)
1048   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
1049   // i.e. first nodes from both arrays determ new diagonal
1050
1051   const SMDS_MeshNode* aNodes[8];
1052   aNodes[0] = N1[0];
1053   aNodes[1] = N1[1];
1054   aNodes[2] = N2[0];
1055   aNodes[3] = N2[1];
1056   aNodes[4] = N1[3];
1057   aNodes[5] = N2[5];
1058   aNodes[6] = N2[3];
1059   aNodes[7] = N1[5];
1060
1061   const SMDS_MeshElement* newElem = 0;
1062   newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
1063                             aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
1064   myLastCreatedElems.push_back(newElem);
1065   AddToSameGroups( newElem, tr1, aMesh );
1066   int aShapeId = tr1->getshapeId();
1067   if ( aShapeId )
1068   {
1069     aMesh->SetMeshElementOnShape( newElem, aShapeId );
1070   }
1071   aMesh->RemoveElement( tr1 );
1072   aMesh->RemoveElement( tr2 );
1073
1074   // remove middle node (9)
1075   GetMeshDS()->RemoveNode( N1[4] );
1076
1077   return true;
1078 }
1079
1080 //=======================================================================
1081 //function : Reorient
1082 //purpose  : Reverse theElement orientation
1083 //=======================================================================
1084
1085 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
1086 {
1087   ClearLastCreated();
1088
1089   if (!theElem)
1090     return false;
1091   SMDS_ElemIteratorPtr it = theElem->nodesIterator();
1092   if ( !it || !it->more() )
1093     return false;
1094
1095   const SMDSAbs_ElementType type = theElem->GetType();
1096   if ( type < SMDSAbs_Edge || type > SMDSAbs_Volume )
1097     return false;
1098
1099   const SMDSAbs_EntityType geomType = theElem->GetEntityType();
1100   if ( geomType == SMDSEntity_Polyhedra ) // polyhedron
1101   {
1102     const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( theElem );
1103     if (!aPolyedre) {
1104       MESSAGE("Warning: bad volumic element");
1105       return false;
1106     }
1107     SMDS_VolumeTool vTool( aPolyedre );
1108     const int nbFaces = vTool.NbFaces();
1109     vector<int> quantities( nbFaces );
1110     vector<const SMDS_MeshNode *> poly_nodes;
1111
1112     // check if all facets are oriented equally
1113     bool sameOri = true;
1114     vector<int>& facetOri = quantities; // keep orientation in quantities so far
1115     for (int iface = 0; iface < nbFaces; iface++)
1116     {
1117       facetOri[ iface ] = vTool.IsFaceExternal( iface );
1118       if ( facetOri[ iface ] != facetOri[ 0 ])
1119         sameOri = false;
1120     }
1121
1122     // reverse faces of the polyhedron
1123     int neededOri = sameOri ? 1 - facetOri[0] : 1;
1124     poly_nodes.reserve( vTool.NbNodes() );
1125     for ( int iface = 0; iface < nbFaces; iface++ )
1126     {
1127       int             nbFaceNodes = vTool.NbFaceNodes( iface );
1128       const SMDS_MeshNode** nodes = vTool.GetFaceNodes( iface );
1129       bool toReverse = ( facetOri[ iface ] != neededOri );
1130
1131       quantities[ iface ] = nbFaceNodes;
1132
1133       if ( toReverse )
1134         for ( int inode = nbFaceNodes - 1; inode >= 0; inode-- )
1135           poly_nodes.push_back( nodes[ inode ]);
1136       else
1137         poly_nodes.insert( poly_nodes.end(), nodes, nodes + nbFaceNodes );
1138     }
1139     return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
1140   }
1141   else // other elements
1142   {
1143     vector<const SMDS_MeshNode*> nodes( theElem->begin_nodes(), theElem->end_nodes() );
1144     const std::vector<int>& interlace = SMDS_MeshCell::reverseSmdsOrder( geomType, nodes.size() );
1145     if ( interlace.empty() )
1146     {
1147       std::reverse( nodes.begin(), nodes.end() ); // obsolete, just in case
1148     }
1149     else
1150     {
1151       SMDS_MeshCell::applyInterlace( interlace, nodes );
1152     }
1153     return GetMeshDS()->ChangeElementNodes( theElem, &nodes[0], nodes.size() );
1154   }
1155   return false;
1156 }
1157
1158 //================================================================================
1159 /*!
1160  * \brief Reorient faces.
1161  * \param theFaces - the faces to reorient. If empty the whole mesh is meant
1162  * \param theDirection - desired direction of normal of \a theFace
1163  * \param theFace - one of \a theFaces that should be oriented according to
1164  *        \a theDirection and whose orientation defines orientation of other faces
1165  * \return number of reoriented faces.
1166  */
1167 //================================================================================
1168
1169 int SMESH_MeshEditor::Reorient2D (TIDSortedElemSet &       theFaces,
1170                                   const gp_Dir&            theDirection,
1171                                   const SMDS_MeshElement * theFace)
1172 {
1173   int nbReori = 0;
1174   if ( !theFace || theFace->GetType() != SMDSAbs_Face ) return nbReori;
1175
1176   if ( theFaces.empty() )
1177   {
1178     SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator(/*idInceasingOrder=true*/);
1179     while ( fIt->more() )
1180       theFaces.insert( theFaces.end(), fIt->next() );
1181   }
1182
1183   // orient theFace according to theDirection
1184   gp_XYZ normal;
1185   SMESH_MeshAlgos::FaceNormal( theFace, normal, /*normalized=*/false );
1186   if ( normal * theDirection.XYZ() < 0 )
1187     nbReori += Reorient( theFace );
1188
1189   // Orient other faces
1190
1191   set< const SMDS_MeshElement* > startFaces, visitedFaces;
1192   TIDSortedElemSet avoidSet;
1193   set< SMESH_TLink > checkedLinks;
1194   pair< set< SMESH_TLink >::iterator, bool > linkIt_isNew;
1195
1196   if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces
1197     theFaces.erase( theFace );
1198   startFaces.insert( theFace );
1199
1200   int nodeInd1, nodeInd2;
1201   const SMDS_MeshElement*           otherFace;
1202   vector< const SMDS_MeshElement* > facesNearLink;
1203   vector< std::pair< int, int > >   nodeIndsOfFace;
1204
1205   set< const SMDS_MeshElement* >::iterator startFace = startFaces.begin();
1206   while ( !startFaces.empty() )
1207   {
1208     startFace = startFaces.begin();
1209     theFace = *startFace;
1210     startFaces.erase( startFace );
1211     if ( !visitedFaces.insert( theFace ).second )
1212       continue;
1213
1214     avoidSet.clear();
1215     avoidSet.insert(theFace);
1216
1217     NLink link( theFace->GetNode( 0 ), (SMDS_MeshNode *) 0 );
1218
1219     const int nbNodes = theFace->NbCornerNodes();
1220     for ( int i = 0; i < nbNodes; ++i ) // loop on links of theFace
1221     {
1222       link.second = theFace->GetNode(( i+1 ) % nbNodes );
1223       linkIt_isNew = checkedLinks.insert( link );
1224       if ( !linkIt_isNew.second )
1225       {
1226         // link has already been checked and won't be encountered more
1227         // if the group (theFaces) is manifold
1228         //checkedLinks.erase( linkIt_isNew.first );
1229       }
1230       else
1231       {
1232         facesNearLink.clear();
1233         nodeIndsOfFace.clear();
1234         while (( otherFace = SMESH_MeshAlgos::FindFaceInSet( link.first, link.second,
1235                                                              theFaces, avoidSet,
1236                                                              &nodeInd1, &nodeInd2 )))
1237           if ( otherFace != theFace)
1238           {
1239             facesNearLink.push_back( otherFace );
1240             nodeIndsOfFace.push_back( make_pair( nodeInd1, nodeInd2 ));
1241             avoidSet.insert( otherFace );
1242           }
1243         if ( facesNearLink.size() > 1 )
1244         {
1245           // NON-MANIFOLD mesh shell !
1246           // select a face most co-directed with theFace,
1247           // other faces won't be visited this time
1248           gp_XYZ NF, NOF;
1249           SMESH_MeshAlgos::FaceNormal( theFace, NF, /*normalized=*/false );
1250           double proj, maxProj = -1;
1251           for ( size_t i = 0; i < facesNearLink.size(); ++i ) {
1252             SMESH_MeshAlgos::FaceNormal( facesNearLink[i], NOF, /*normalized=*/false );
1253             if (( proj = Abs( NF * NOF )) > maxProj ) {
1254               maxProj = proj;
1255               otherFace = facesNearLink[i];
1256               nodeInd1  = nodeIndsOfFace[i].first;
1257               nodeInd2  = nodeIndsOfFace[i].second;
1258             }
1259           }
1260           // not to visit rejected faces
1261           for ( size_t i = 0; i < facesNearLink.size(); ++i )
1262             if ( facesNearLink[i] != otherFace && theFaces.size() > 1 )
1263               visitedFaces.insert( facesNearLink[i] );
1264         }
1265         else if ( facesNearLink.size() == 1 )
1266         {
1267           otherFace = facesNearLink[0];
1268           nodeInd1  = nodeIndsOfFace.back().first;
1269           nodeInd2  = nodeIndsOfFace.back().second;
1270         }
1271         if ( otherFace && otherFace != theFace)
1272         {
1273           // link must be reverse in otherFace if orientation to otherFace
1274           // is same as that of theFace
1275           if ( abs(nodeInd2-nodeInd1) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1276           {
1277             nbReori += Reorient( otherFace );
1278           }
1279           startFaces.insert( otherFace );
1280         }
1281       }
1282       std::swap( link.first, link.second ); // reverse the link
1283     }
1284   }
1285   return nbReori;
1286 }
1287
1288 //================================================================================
1289 /*!
1290  * \brief Reorient faces basing on orientation of adjacent volumes.
1291  * \param theFaces - faces to reorient. If empty, all mesh faces are treated.
1292  * \param theVolumes - reference volumes.
1293  * \param theOutsideNormal - to orient faces to have their normal
1294  *        pointing either \a outside or \a inside the adjacent volumes.
1295  * \return number of reoriented faces.
1296  */
1297 //================================================================================
1298
1299 int SMESH_MeshEditor::Reorient2DBy3D (TIDSortedElemSet & theFaces,
1300                                       TIDSortedElemSet & theVolumes,
1301                                       const bool         theOutsideNormal)
1302 {
1303   int nbReori = 0;
1304
1305   SMDS_ElemIteratorPtr faceIt;
1306   if ( theFaces.empty() )
1307     faceIt = GetMeshDS()->elementsIterator( SMDSAbs_Face );
1308   else
1309     faceIt = SMESHUtils::elemSetIterator( theFaces );
1310
1311   vector< const SMDS_MeshNode* > faceNodes;
1312   TIDSortedElemSet checkedVolumes;
1313   set< const SMDS_MeshNode* > faceNodesSet;
1314   SMDS_VolumeTool volumeTool;
1315
1316   while ( faceIt->more() ) // loop on given faces
1317   {
1318     const SMDS_MeshElement* face = faceIt->next();
1319     if ( face->GetType() != SMDSAbs_Face )
1320       continue;
1321
1322     const size_t nbCornersNodes = face->NbCornerNodes();
1323     faceNodes.assign( face->begin_nodes(), face->end_nodes() );
1324
1325     checkedVolumes.clear();
1326     SMDS_ElemIteratorPtr vIt = faceNodes[ 0 ]->GetInverseElementIterator( SMDSAbs_Volume );
1327     while ( vIt->more() )
1328     {
1329       const SMDS_MeshElement* volume = vIt->next();
1330
1331       if ( !checkedVolumes.insert( volume ).second )
1332         continue;
1333       if ( !theVolumes.empty() && !theVolumes.count( volume ))
1334         continue;
1335
1336       // is volume adjacent?
1337       bool allNodesCommon = true;
1338       for ( size_t iN = 1; iN < nbCornersNodes && allNodesCommon; ++iN )
1339         allNodesCommon = ( volume->GetNodeIndex( faceNodes[ iN ]) > -1 );
1340       if ( !allNodesCommon )
1341         continue;
1342
1343       // get nodes of a corresponding volume facet
1344       faceNodesSet.clear();
1345       faceNodesSet.insert( faceNodes.begin(), faceNodes.end() );
1346       volumeTool.Set( volume );
1347       int facetID = volumeTool.GetFaceIndex( faceNodesSet );
1348       if ( facetID < 0 ) continue;
1349       volumeTool.SetExternalNormal();
1350       const SMDS_MeshNode** facetNodes = volumeTool.GetFaceNodes( facetID );
1351
1352       // compare order of faceNodes and facetNodes
1353       const int iQ = 1 + ( nbCornersNodes < faceNodes.size() );
1354       int iNN[2];
1355       for ( int i = 0; i < 2; ++i )
1356       {
1357         const SMDS_MeshNode* n = facetNodes[ i*iQ ];
1358         for ( size_t iN = 0; iN < nbCornersNodes; ++iN )
1359           if ( faceNodes[ iN ] == n )
1360           {
1361             iNN[ i ] = iN;
1362             break;
1363           }
1364       }
1365       bool isOutside = Abs( iNN[0]-iNN[1] ) == 1 ? iNN[0] < iNN[1] : iNN[0] > iNN[1];
1366       if ( isOutside != theOutsideNormal )
1367         nbReori += Reorient( face );
1368     }
1369   }  // loop on given faces
1370
1371   return nbReori;
1372 }
1373
1374 //=======================================================================
1375 //function : getBadRate
1376 //purpose  :
1377 //=======================================================================
1378
1379 static double getBadRate (const SMDS_MeshElement*               theElem,
1380                           SMESH::Controls::NumericalFunctorPtr& theCrit)
1381 {
1382   SMESH::Controls::TSequenceOfXYZ P;
1383   if ( !theElem || !theCrit->GetPoints( theElem, P ))
1384     return 1e100;
1385   return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1386   //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1387 }
1388
1389 //=======================================================================
1390 //function : QuadToTri
1391 //purpose  : Cut quadrangles into triangles.
1392 //           theCrit is used to select a diagonal to cut
1393 //=======================================================================
1394
1395 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet &                   theElems,
1396                                   SMESH::Controls::NumericalFunctorPtr theCrit)
1397 {
1398   ClearLastCreated();
1399
1400   if ( !theCrit.get() )
1401     return false;
1402
1403   SMESHDS_Mesh *       aMesh = GetMeshDS();
1404   Handle(Geom_Surface) surface;
1405   SMESH_MesherHelper   helper( *GetMesh() );
1406
1407   myLastCreatedElems.reserve( theElems.size() * 2 );
1408
1409   TIDSortedElemSet::iterator itElem;
1410   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
1411   {
1412     const SMDS_MeshElement* elem = *itElem;
1413     if ( !elem || elem->GetType() != SMDSAbs_Face )
1414       continue;
1415     if ( elem->NbCornerNodes() != 4 )
1416       continue;
1417
1418     // retrieve element nodes
1419     vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1420
1421     // compare two sets of possible triangles
1422     double aBadRate1, aBadRate2; // to what extent a set is bad
1423     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1424     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1425     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1426
1427     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1428     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1429     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1430
1431     const int aShapeId = FindShape( elem );
1432     const SMDS_MeshElement* newElem1 = 0;
1433     const SMDS_MeshElement* newElem2 = 0;
1434
1435     if ( !elem->IsQuadratic() ) // split linear quadrangle
1436     {
1437       // for MaxElementLength2D functor we return minimum diagonal for splitting,
1438       // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1439       if ( aBadRate1 <= aBadRate2 ) {
1440         // tr1 + tr2 is better
1441         newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1442         newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1443       }
1444       else {
1445         // tr3 + tr4 is better
1446         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1447         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1448       }
1449     }
1450     else // split quadratic quadrangle
1451     {
1452       helper.SetIsQuadratic( true );
1453       helper.SetIsBiQuadratic( aNodes.size() == 9 );
1454
1455       helper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem ));
1456       if ( aNodes.size() == 9 )
1457       {
1458         helper.SetIsBiQuadratic( true );
1459         if ( aBadRate1 <= aBadRate2 )
1460           helper.AddTLinkNode( aNodes[0], aNodes[2], aNodes[8] );
1461         else
1462           helper.AddTLinkNode( aNodes[1], aNodes[3], aNodes[8] );
1463       }
1464       // create a new element
1465       if ( aBadRate1 <= aBadRate2 ) {
1466         newElem1 = helper.AddFace( aNodes[2], aNodes[3], aNodes[0] );
1467         newElem2 = helper.AddFace( aNodes[2], aNodes[0], aNodes[1] );
1468       }
1469       else {
1470         newElem1 = helper.AddFace( aNodes[3], aNodes[0], aNodes[1] );
1471         newElem2 = helper.AddFace( aNodes[3], aNodes[1], aNodes[2] );
1472       }
1473     } // quadratic case
1474
1475     // care of a new element
1476
1477     myLastCreatedElems.push_back(newElem1);
1478     myLastCreatedElems.push_back(newElem2);
1479     AddToSameGroups( newElem1, elem, aMesh );
1480     AddToSameGroups( newElem2, elem, aMesh );
1481
1482     // put a new triangle on the same shape
1483     if ( aShapeId )
1484       aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1485     aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1486
1487     aMesh->RemoveElement( elem );
1488   }
1489   return true;
1490 }
1491
1492 //=======================================================================
1493 /*!
1494  * \brief Split each of given quadrangles into 4 triangles.
1495  * \param theElems - The faces to be split. If empty all faces are split.
1496  */
1497 //=======================================================================
1498
1499 void SMESH_MeshEditor::QuadTo4Tri (TIDSortedElemSet & theElems)
1500 {
1501   ClearLastCreated();
1502   myLastCreatedElems.reserve( theElems.size() * 4 );
1503
1504   SMESH_MesherHelper helper( *GetMesh() );
1505   helper.SetElementsOnShape( true );
1506
1507   // get standalone groups of faces
1508   vector< SMDS_MeshGroup* > allFaceGroups, faceGroups;
1509   for ( SMESHDS_GroupBase* grBase : GetMeshDS()->GetGroups() )
1510     if ( SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( grBase ))
1511       if ( group->GetType() == SMDSAbs_Face && !group->IsEmpty() )
1512         allFaceGroups.push_back( & group->SMDSGroup() );
1513
1514   bool   checkUV;
1515   gp_XY  uv [9]; uv[8] = gp_XY(0,0);
1516   gp_XYZ xyz[9];
1517   vector< const SMDS_MeshNode* > nodes;
1518   SMESHDS_SubMesh*               subMeshDS = 0;
1519   TopoDS_Face                    F;
1520   Handle(Geom_Surface)           surface;
1521   TopLoc_Location                loc;
1522
1523   SMDS_ElemIteratorPtr faceIt;
1524   if ( theElems.empty() ) faceIt = GetMeshDS()->elementsIterator(SMDSAbs_Face);
1525   else                    faceIt = SMESHUtils::elemSetIterator( theElems );
1526
1527   while ( faceIt->more() )
1528   {
1529     const SMDS_MeshElement* quad = faceIt->next();
1530     if ( !quad || quad->NbCornerNodes() != 4 )
1531       continue;
1532
1533     // get a surface the quad is on
1534
1535     if ( quad->getshapeId() < 1 )
1536     {
1537       F.Nullify();
1538       helper.SetSubShape( 0 );
1539       subMeshDS = 0;
1540     }
1541     else if ( quad->getshapeId() != helper.GetSubShapeID() )
1542     {
1543       helper.SetSubShape( quad->getshapeId() );
1544       if ( !helper.GetSubShape().IsNull() &&
1545            helper.GetSubShape().ShapeType() == TopAbs_FACE )
1546       {
1547         F = TopoDS::Face( helper.GetSubShape() );
1548         surface = BRep_Tool::Surface( F, loc );
1549         subMeshDS = GetMeshDS()->MeshElements( quad->getshapeId() );
1550       }
1551       else
1552       {
1553         helper.SetSubShape( 0 );
1554         subMeshDS = 0;
1555       }
1556     }
1557
1558     // create a central node
1559
1560     const SMDS_MeshNode* nCentral;
1561     nodes.assign( quad->begin_nodes(), quad->end_nodes() );
1562
1563     if ( nodes.size() == 9 )
1564     {
1565       nCentral = nodes.back();
1566     }
1567     else
1568     {
1569       size_t iN = 0;
1570       if ( F.IsNull() )
1571       {
1572         for ( ; iN < nodes.size(); ++iN )
1573           xyz[ iN ] = SMESH_NodeXYZ( nodes[ iN ] );
1574
1575         for ( ; iN < 8; ++iN ) // mid-side points of a linear qudrangle
1576           xyz[ iN ] = 0.5 * ( xyz[ iN - 4 ] + xyz[( iN - 3 )%4 ] );
1577
1578         xyz[ 8 ] = helper.calcTFI( 0.5, 0.5,
1579                                    xyz[0], xyz[1], xyz[2], xyz[3],
1580                                    xyz[4], xyz[5], xyz[6], xyz[7] );
1581       }
1582       else
1583       {
1584         for ( ; iN < nodes.size(); ++iN )
1585           uv[ iN ] = helper.GetNodeUV( F, nodes[iN], nodes[(iN+2)%4], &checkUV );
1586
1587         for ( ; iN < 8; ++iN ) // UV of mid-side points of a linear qudrangle
1588           uv[ iN ] = helper.GetMiddleUV( surface, uv[ iN - 4 ], uv[( iN - 3 )%4 ] );
1589
1590         uv[ 8 ] = helper.calcTFI( 0.5, 0.5,
1591                                   uv[0], uv[1], uv[2], uv[3],
1592                                   uv[4], uv[5], uv[6], uv[7] );
1593
1594         gp_Pnt p = surface->Value( uv[8].X(), uv[8].Y() ).Transformed( loc );
1595         xyz[ 8 ] = p.XYZ();
1596       }
1597
1598       nCentral = helper.AddNode( xyz[8].X(), xyz[8].Y(), xyz[8].Z(), /*id=*/0,
1599                                  uv[8].X(), uv[8].Y() );
1600       myLastCreatedNodes.push_back( nCentral );
1601     }
1602
1603     helper.SetIsQuadratic  ( nodes.size() > 4 );
1604     helper.SetIsBiQuadratic( nodes.size() == 9 );
1605     if ( helper.GetIsQuadratic() )
1606       helper.AddTLinks( static_cast< const SMDS_MeshFace*>( quad ));
1607
1608     // select groups to update
1609     faceGroups.clear();
1610     for ( SMDS_MeshGroup* group : allFaceGroups )
1611       if ( group->Remove( quad ))
1612         faceGroups.push_back( group );
1613
1614     // create 4 triangles
1615
1616     GetMeshDS()->RemoveFreeElement( quad, subMeshDS, /*fromGroups=*/false );
1617
1618     for ( int i = 0; i < 4; ++i )
1619     {
1620       SMDS_MeshElement* tria = helper.AddFace( nodes[ i ],
1621                                                nodes[(i+1)%4],
1622                                                nCentral );
1623       myLastCreatedElems.push_back( tria );
1624       for ( SMDS_MeshGroup* group : faceGroups )
1625         group->Add( tria );
1626     }
1627   }
1628 }
1629
1630 //=======================================================================
1631 //function : BestSplit
1632 //purpose  : Find better diagonal for cutting.
1633 //=======================================================================
1634
1635 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement*              theQuad,
1636                                  SMESH::Controls::NumericalFunctorPtr theCrit)
1637 {
1638   ClearLastCreated();
1639
1640   if (!theCrit.get())
1641     return -1;
1642
1643   if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1644     return -1;
1645
1646   if( theQuad->NbNodes()==4 ||
1647       (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1648
1649     // retrieve element nodes
1650     const SMDS_MeshNode* aNodes [4];
1651     SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1652     int i = 0;
1653     //while (itN->more())
1654     while (i<4) {
1655       aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1656     }
1657     // compare two sets of possible triangles
1658     double aBadRate1, aBadRate2; // to what extent a set is bad
1659     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1660     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1661     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1662
1663     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1664     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1665     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1666     // for MaxElementLength2D functor we return minimum diagonal for splitting,
1667     // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1668     if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1669       return 1; // diagonal 1-3
1670
1671     return 2; // diagonal 2-4
1672   }
1673   return -1;
1674 }
1675
1676 namespace
1677 {
1678   // Methods of splitting volumes into tetra
1679
1680   const int theHexTo5_1[5*4+1] =
1681     {
1682       0, 1, 2, 5,    0, 4, 5, 7,     0, 2, 3, 7,    2, 5, 6, 7,     0, 5, 2, 7,   -1
1683     };
1684   const int theHexTo5_2[5*4+1] =
1685     {
1686       1, 2, 3, 6,    1, 4, 5, 6,     0, 1, 3, 4,    3, 4, 6, 7,     1, 3, 4, 6,   -1
1687     };
1688   const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1689
1690   const int theHexTo6_1[6*4+1] =
1691     {
1692       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
1693     };
1694   const int theHexTo6_2[6*4+1] =
1695     {
1696       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
1697     };
1698   const int theHexTo6_3[6*4+1] =
1699     {
1700       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
1701     };
1702   const int theHexTo6_4[6*4+1] =
1703     {
1704       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
1705     };
1706   const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1707
1708   const int thePyraTo2_1[2*4+1] =
1709     {
1710       0, 1, 2, 4,    0, 2, 3, 4,   -1
1711     };
1712   const int thePyraTo2_2[2*4+1] =
1713     {
1714       1, 2, 3, 4,    1, 3, 0, 4,   -1
1715     };
1716   const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1717
1718   const int thePentaTo3_1[3*4+1] =
1719     {
1720       0, 1, 2, 3,    1, 3, 4, 2,     2, 3, 4, 5,    -1
1721     };
1722   const int thePentaTo3_2[3*4+1] =
1723     {
1724       1, 2, 0, 4,    2, 4, 5, 0,     0, 4, 5, 3,    -1
1725     };
1726   const int thePentaTo3_3[3*4+1] =
1727     {
1728       2, 0, 1, 5,    0, 5, 3, 1,     1, 5, 3, 4,    -1
1729     };
1730   const int thePentaTo3_4[3*4+1] =
1731     {
1732       0, 1, 2, 3,    1, 3, 4, 5,     2, 3, 1, 5,    -1
1733     };
1734   const int thePentaTo3_5[3*4+1] =
1735     {
1736       1, 2, 0, 4,    2, 4, 5, 3,     0, 4, 2, 3,    -1
1737     };
1738   const int thePentaTo3_6[3*4+1] =
1739     {
1740       2, 0, 1, 5,    0, 5, 3, 4,     1, 5, 0, 4,    -1
1741     };
1742   const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1743                                 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1744
1745   // Methods of splitting hexahedron into prisms
1746
1747   const int theHexTo4Prisms_BT[6*4+1] = // bottom-top
1748     {
1749       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
1750     };
1751   const int theHexTo4Prisms_LR[6*4+1] = // left-right
1752     {
1753       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
1754     };
1755   const int theHexTo4Prisms_FB[6*4+1] = // front-back
1756     {
1757       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
1758     };
1759
1760   const int theHexTo2Prisms_BT_1[6*2+1] =
1761     {
1762       0, 1, 3, 4, 5, 7,    1, 2, 3, 5, 6, 7,   -1
1763     };
1764   const int theHexTo2Prisms_BT_2[6*2+1] =
1765     {
1766       0, 1, 2, 4, 5, 6,    0, 2, 3, 4, 6, 7,   -1
1767     };
1768   const int* theHexTo2Prisms_BT[2] = { theHexTo2Prisms_BT_1, theHexTo2Prisms_BT_2 };
1769
1770   const int theHexTo2Prisms_LR_1[6*2+1] =
1771     {
1772       1, 0, 4, 2, 3, 7,    1, 4, 5, 2, 7, 6,   -1
1773     };
1774   const int theHexTo2Prisms_LR_2[6*2+1] =
1775     {
1776       1, 0, 4, 2, 3, 7,    1, 4, 5, 2, 7, 6,   -1
1777     };
1778   const int* theHexTo2Prisms_LR[2] = { theHexTo2Prisms_LR_1, theHexTo2Prisms_LR_2 };
1779
1780   const int theHexTo2Prisms_FB_1[6*2+1] =
1781     {
1782       0, 3, 4, 1, 2, 5,    3, 7, 4, 2, 6, 5,   -1
1783     };
1784   const int theHexTo2Prisms_FB_2[6*2+1] =
1785     {
1786       0, 3, 7, 1, 2, 7,    0, 7, 4, 1, 6, 5,   -1
1787     };
1788   const int* theHexTo2Prisms_FB[2] = { theHexTo2Prisms_FB_1, theHexTo2Prisms_FB_2 };
1789
1790
1791   struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1792   {
1793     int _n1, _n2, _n3;
1794     TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1795     bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1796     bool hasAdjacentVol( const SMDS_MeshElement*    elem,
1797                          const SMDSAbs_GeometryType geom = SMDSGeom_TETRA) const;
1798   };
1799   struct TSplitMethod
1800   {
1801     int        _nbSplits;
1802     int        _nbCorners;
1803     const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1804     bool       _baryNode;     //!< additional node is to be created at cell barycenter
1805     bool       _ownConn;      //!< to delete _connectivity in destructor
1806     map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1807
1808     TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1809       : _nbSplits(nbTet), _nbCorners(4), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1810     ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1811     TSplitMethod(const TSplitMethod &splitMethod)
1812       : _nbSplits(splitMethod._nbSplits),
1813         _nbCorners(splitMethod._nbCorners),
1814         _baryNode(splitMethod._baryNode),
1815         _ownConn(splitMethod._ownConn),
1816         _faceBaryNode(splitMethod._faceBaryNode)
1817     {
1818       _connectivity = splitMethod._connectivity;
1819       const_cast<TSplitMethod&>(splitMethod)._connectivity = nullptr;
1820       const_cast<TSplitMethod&>(splitMethod)._ownConn = false;
1821     }
1822     bool hasFacet( const TTriangleFacet& facet ) const
1823     {
1824       if ( _nbCorners == 4 )
1825       {
1826         const int* tetConn = _connectivity;
1827         for ( ; tetConn[0] >= 0; tetConn += 4 )
1828           if (( facet.contains( tetConn[0] ) +
1829                 facet.contains( tetConn[1] ) +
1830                 facet.contains( tetConn[2] ) +
1831                 facet.contains( tetConn[3] )) == 3 )
1832             return true;
1833       }
1834       else // prism, _nbCorners == 6
1835       {
1836         const int* prismConn = _connectivity;
1837         for ( ; prismConn[0] >= 0; prismConn += 6 )
1838         {
1839           if (( facet.contains( prismConn[0] ) &&
1840                 facet.contains( prismConn[1] ) &&
1841                 facet.contains( prismConn[2] ))
1842               ||
1843               ( facet.contains( prismConn[3] ) &&
1844                 facet.contains( prismConn[4] ) &&
1845                 facet.contains( prismConn[5] )))
1846             return true;
1847         }
1848       }
1849       return false;
1850     }
1851   };
1852
1853   //=======================================================================
1854   /*!
1855    * \brief return TSplitMethod for the given element to split into tetrahedra
1856    */
1857   //=======================================================================
1858
1859   TSplitMethod getTetraSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1860   {
1861     const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1862
1863     // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1864     // an edge and a face barycenter; tertaherdons are based on triangles and
1865     // a volume barycenter
1866     const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1867
1868     // Find out how adjacent volumes are split
1869
1870     vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1871     int hasAdjacentSplits = 0, maxTetConnSize = 0;
1872     for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1873     {
1874       int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1875       maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1876       if ( nbNodes < 4 ) continue;
1877
1878       list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1879       const int* nInd = vol.GetFaceNodesIndices( iF );
1880       if ( nbNodes == 4 )
1881       {
1882         TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1883         TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1884         if      ( t012.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t012 );
1885         else if ( t123.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t123 );
1886       }
1887       else
1888       {
1889         int iCom = 0; // common node of triangle faces to split into
1890         for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1891         {
1892           TTriangleFacet t012( nInd[ iQ * ( iCom             )],
1893                                nInd[ iQ * ( (iCom+1)%nbNodes )],
1894                                nInd[ iQ * ( (iCom+2)%nbNodes )]);
1895           TTriangleFacet t023( nInd[ iQ * ( iCom             )],
1896                                nInd[ iQ * ( (iCom+2)%nbNodes )],
1897                                nInd[ iQ * ( (iCom+3)%nbNodes )]);
1898           if ( t012.hasAdjacentVol( vol.Element() ) && t023.hasAdjacentVol( vol.Element() ))
1899           {
1900             triaSplits.push_back( t012 );
1901             triaSplits.push_back( t023 );
1902             break;
1903           }
1904         }
1905       }
1906       if ( !triaSplits.empty() )
1907         hasAdjacentSplits = true;
1908     }
1909
1910     // Among variants of split method select one compliant with adjacent volumes
1911
1912     TSplitMethod method;
1913     if ( !vol.Element()->IsPoly() && !is24TetMode )
1914     {
1915       int nbVariants = 2, nbTet = 0;
1916       const int** connVariants = 0;
1917       switch ( vol.Element()->GetEntityType() )
1918       {
1919       case SMDSEntity_Hexa:
1920       case SMDSEntity_Quad_Hexa:
1921       case SMDSEntity_TriQuad_Hexa:
1922         if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1923           connVariants = theHexTo5, nbTet = 5;
1924         else
1925           connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1926         break;
1927       case SMDSEntity_Pyramid:
1928       case SMDSEntity_Quad_Pyramid:
1929         connVariants = thePyraTo2;  nbTet = 2;
1930         break;
1931       case SMDSEntity_Penta:
1932       case SMDSEntity_Quad_Penta:
1933       case SMDSEntity_BiQuad_Penta:
1934         connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1935         break;
1936       default:
1937         nbVariants = 0;
1938       }
1939       for ( int variant = 0; variant < nbVariants && method._nbSplits == 0; ++variant )
1940       {
1941         // check method compliance with adjacent tetras,
1942         // all found splits must be among facets of tetras described by this method
1943         method = TSplitMethod( nbTet, connVariants[variant] );
1944         if ( hasAdjacentSplits && method._nbSplits > 0 )
1945         {
1946           bool facetCreated = true;
1947           for ( size_t iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1948           {
1949             list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1950             for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1951               facetCreated = method.hasFacet( *facet );
1952           }
1953           if ( !facetCreated )
1954             method = TSplitMethod(0); // incompatible method
1955         }
1956       }
1957     }
1958     if ( method._nbSplits < 1 )
1959     {
1960       // No standard method is applicable, use a generic solution:
1961       // each facet of a volume is split into triangles and
1962       // each of triangles and a volume barycenter form a tetrahedron.
1963
1964       const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1965
1966       int* connectivity = new int[ maxTetConnSize + 1 ];
1967       method._connectivity = connectivity;
1968       method._ownConn = true;
1969       method._baryNode = !isHex27; // to create central node or not
1970
1971       int connSize = 0;
1972       int baryCenInd = vol.NbNodes() - int( isHex27 );
1973       for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1974       {
1975         const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1976         const int*   nInd = vol.GetFaceNodesIndices( iF );
1977         // find common node of triangle facets of tetra to create
1978         int iCommon = 0; // index in linear numeration
1979         const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1980         if ( !triaSplits.empty() )
1981         {
1982           // by found facets
1983           const TTriangleFacet* facet = &triaSplits.front();
1984           for ( ; iCommon < nbNodes-1 ; ++iCommon )
1985             if ( facet->contains( nInd[ iQ * iCommon ]) &&
1986                  facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1987               break;
1988         }
1989         else if ( nbNodes > 3 && !is24TetMode )
1990         {
1991           // find the best method of splitting into triangles by aspect ratio
1992           SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1993           map< double, int > badness2iCommon;
1994           const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1995           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1996           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1997           {
1998             double badness = 0;
1999             for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
2000             {
2001               SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon         )],
2002                                       nodes[ iQ*((iLast-1)%nbNodes)],
2003                                       nodes[ iQ*((iLast  )%nbNodes)]);
2004               badness += getBadRate( &tria, aspectRatio );
2005             }
2006             badness2iCommon.insert( make_pair( badness, iCommon ));
2007           }
2008           // use iCommon with lowest badness
2009           iCommon = badness2iCommon.begin()->second;
2010         }
2011         if ( iCommon >= nbNodes )
2012           iCommon = 0; // something wrong
2013
2014         // fill connectivity of tetrahedra based on a current face
2015         int nbTet = nbNodes - 2;
2016         if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
2017         {
2018           int faceBaryCenInd;
2019           if ( isHex27 )
2020           {
2021             faceBaryCenInd = vol.GetCenterNodeIndex( iF );
2022             method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
2023           }
2024           else
2025           {
2026             method._faceBaryNode[ iF ] = 0;
2027             faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
2028           }
2029           nbTet = nbNodes;
2030           for ( int i = 0; i < nbTet; ++i )
2031           {
2032             int i1 = i, i2 = (i+1) % nbNodes;
2033             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2034             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2035             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2036             connectivity[ connSize++ ] = faceBaryCenInd;
2037             connectivity[ connSize++ ] = baryCenInd;
2038           }
2039         }
2040         else
2041         {
2042           for ( int i = 0; i < nbTet; ++i )
2043           {
2044             int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
2045             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2046             connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
2047             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2048             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2049             connectivity[ connSize++ ] = baryCenInd;
2050           }
2051         }
2052         method._nbSplits += nbTet;
2053
2054       } // loop on volume faces
2055
2056       connectivity[ connSize++ ] = -1;
2057
2058     } // end of generic solution
2059
2060     return method;
2061   }
2062   //=======================================================================
2063   /*!
2064    * \brief return TSplitMethod to split haxhedron into prisms
2065    */
2066   //=======================================================================
2067
2068   TSplitMethod getPrismSplitMethod( SMDS_VolumeTool& vol,
2069                                     const int        methodFlags,
2070                                     const int        facetToSplit)
2071   {
2072     TSplitMethod method;
2073
2074     // order of facets in HEX according to SMDS_VolumeTool::Hexa_F :
2075     // B, T, L, B, R, F
2076     const int iF = ( facetToSplit < 2 ) ? 0 : 1 + ( facetToSplit-2 ) % 2; // [0,1,2]
2077
2078     if ( methodFlags == SMESH_MeshEditor::HEXA_TO_4_PRISMS )
2079     {
2080       static TSplitMethod to4methods[4]; // order BT, LR, FB
2081       if ( to4methods[iF]._nbSplits == 0 )
2082       {
2083         switch ( iF ) {
2084         case 0:
2085           to4methods[iF]._connectivity = theHexTo4Prisms_BT;
2086           to4methods[iF]._faceBaryNode[ 0 ] = 0;
2087           to4methods[iF]._faceBaryNode[ 1 ] = 0;
2088           break;
2089         case 1:
2090           to4methods[iF]._connectivity = theHexTo4Prisms_LR;
2091           to4methods[iF]._faceBaryNode[ 2 ] = 0;
2092           to4methods[iF]._faceBaryNode[ 4 ] = 0;
2093           break;
2094         case 2:
2095           to4methods[iF]._connectivity = theHexTo4Prisms_FB;
2096           to4methods[iF]._faceBaryNode[ 3 ] = 0;
2097           to4methods[iF]._faceBaryNode[ 5 ] = 0;
2098           break;
2099         default: return to4methods[3];
2100         }
2101         to4methods[iF]._nbSplits  = 4;
2102         to4methods[iF]._nbCorners = 6;
2103       }
2104       method = to4methods[iF];
2105       to4methods[iF]._connectivity = method._connectivity; // as copy ctor resets _connectivity
2106       return method;
2107     }
2108     // else if ( methodFlags == HEXA_TO_2_PRISMS )
2109
2110     const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2111
2112     const int nbVariants = 2, nbSplits = 2;
2113     const int** connVariants = 0;
2114     switch ( iF ) {
2115     case 0: connVariants = theHexTo2Prisms_BT; break;
2116     case 1: connVariants = theHexTo2Prisms_LR; break;
2117     case 2: connVariants = theHexTo2Prisms_FB; break;
2118     default: return method;
2119     }
2120
2121     // look for prisms adjacent via facetToSplit and an opposite one
2122     for ( int is2nd = 0; is2nd < 2; ++is2nd )
2123     {
2124       int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2125       int nbNodes = vol.NbFaceNodes( iFacet ) / iQ;
2126       if ( nbNodes != 4 ) return method;
2127
2128       const int* nInd = vol.GetFaceNodesIndices( iFacet );
2129       TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2130       TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2131       TTriangleFacet* t;
2132       if      ( t012.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2133         t = &t012;
2134       else if ( t123.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2135         t = &t123;
2136       else
2137         continue;
2138
2139       // there are adjacent prism
2140       for ( int variant = 0; variant < nbVariants; ++variant )
2141       {
2142         // check method compliance with adjacent prisms,
2143         // the found prism facets must be among facets of prisms described by current method
2144         method._nbSplits     = nbSplits;
2145         method._nbCorners    = 6;
2146         method._connectivity = connVariants[ variant ];
2147         if ( method.hasFacet( *t ))
2148           return method;
2149       }
2150     }
2151
2152     // No adjacent prisms. Select a variant with a best aspect ratio.
2153
2154     double badness[2] = { 0., 0. };
2155     static SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
2156     const SMDS_MeshNode** nodes = vol.GetNodes();
2157     for ( int variant = 0; variant < nbVariants; ++variant )
2158       for ( int is2nd = 0; is2nd < 2; ++is2nd )
2159       {
2160         int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2161         const int*             nInd = vol.GetFaceNodesIndices( iFacet );
2162
2163         method._connectivity = connVariants[ variant ];
2164         TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2165         TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2166         TTriangleFacet* t = ( method.hasFacet( t012 )) ? & t012 : & t123;
2167
2168         SMDS_FaceOfNodes tria ( nodes[ t->_n1 ],
2169                                 nodes[ t->_n2 ],
2170                                 nodes[ t->_n3 ] );
2171         badness[ variant ] += getBadRate( &tria, aspectRatio );
2172       }
2173     const int iBetter = ( badness[1] < badness[0] && badness[0]-badness[1] > 0.1 * badness[0] );
2174
2175     method._nbSplits     = nbSplits;
2176     method._nbCorners    = 6;
2177     method._connectivity = connVariants[ iBetter ];
2178
2179     return method;
2180   }
2181
2182   //================================================================================
2183   /*!
2184    * \brief Check if there is a tetraherdon adjacent to the given element via this facet
2185    */
2186   //================================================================================
2187
2188   bool TTriangleFacet::hasAdjacentVol( const SMDS_MeshElement*    elem,
2189                                        const SMDSAbs_GeometryType geom ) const
2190   {
2191     // find the tetrahedron including the three nodes of facet
2192     const SMDS_MeshNode* n1 = elem->GetNode(_n1);
2193     const SMDS_MeshNode* n2 = elem->GetNode(_n2);
2194     const SMDS_MeshNode* n3 = elem->GetNode(_n3);
2195     SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
2196     while ( volIt1->more() )
2197     {
2198       const SMDS_MeshElement* v = volIt1->next();
2199       if ( v->GetGeomType() != geom )
2200         continue;
2201       const int lastCornerInd = v->NbCornerNodes() - 1;
2202       if ( v->IsQuadratic() && v->GetNodeIndex( n1 ) > lastCornerInd )
2203         continue; // medium node not allowed
2204       const int ind2 = v->GetNodeIndex( n2 );
2205       if ( ind2 < 0 || lastCornerInd < ind2 )
2206         continue;
2207       const int ind3 = v->GetNodeIndex( n3 );
2208       if ( ind3 < 0 || lastCornerInd < ind3 )
2209         continue;
2210       return true;
2211     }
2212     return false;
2213   }
2214
2215   //=======================================================================
2216   /*!
2217    * \brief A key of a face of volume
2218    */
2219   //=======================================================================
2220
2221   struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
2222   {
2223     TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
2224     {
2225       TIDSortedNodeSet sortedNodes;
2226       const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2227       int nbNodes = vol.NbFaceNodes( iF );
2228       const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
2229       for ( int i = 0; i < nbNodes; i += iQ )
2230         sortedNodes.insert( fNodes[i] );
2231       TIDSortedNodeSet::iterator n = sortedNodes.begin();
2232       first.first   = (*(n++))->GetID();
2233       first.second  = (*(n++))->GetID();
2234       second.first  = (*(n++))->GetID();
2235       second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
2236     }
2237   };
2238 } // namespace
2239
2240 //=======================================================================
2241 //function : SplitVolumes
2242 //purpose  : Split volume elements into tetrahedra or prisms.
2243 //           If facet ID < 0, element is split into tetrahedra,
2244 //           else a hexahedron is split into prisms so that the given facet is
2245 //           split into triangles
2246 //=======================================================================
2247
2248 void SMESH_MeshEditor::SplitVolumes (const TFacetOfElem & theElems,
2249                                      const int            theMethodFlags)
2250 {
2251   SMDS_VolumeTool    volTool;
2252   SMESH_MesherHelper helper( *GetMesh()), fHelper(*GetMesh());
2253   fHelper.ToFixNodeParameters( true );
2254
2255   SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
2256   SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
2257
2258   SMESH_SequenceOfElemPtr newNodes, newElems;
2259
2260   // map face of volume to it's baricenrtic node
2261   map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
2262   double bc[3];
2263   vector<const SMDS_MeshElement* > splitVols;
2264
2265   TFacetOfElem::const_iterator elem2facet = theElems.begin();
2266   for ( ; elem2facet != theElems.end(); ++elem2facet )
2267   {
2268     const SMDS_MeshElement* elem = elem2facet->first;
2269     const int       facetToSplit = elem2facet->second;
2270     if ( elem->GetType() != SMDSAbs_Volume )
2271       continue;
2272     const SMDSAbs_EntityType geomType = elem->GetEntityType();
2273     if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
2274       continue;
2275
2276     if ( !volTool.Set( elem, /*ignoreCentralNodes=*/false )) continue; // strange...
2277
2278     TSplitMethod splitMethod = ( facetToSplit < 0  ?
2279                                  getTetraSplitMethod( volTool, theMethodFlags ) :
2280                                  getPrismSplitMethod( volTool, theMethodFlags, facetToSplit ));
2281     if ( splitMethod._nbSplits < 1 ) continue;
2282
2283     // find submesh to add new tetras to
2284     if ( !subMesh || !subMesh->Contains( elem ))
2285     {
2286       int shapeID = FindShape( elem );
2287       helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
2288       subMesh = GetMeshDS()->MeshElements( shapeID );
2289     }
2290     int iQ;
2291     if ( elem->IsQuadratic() )
2292     {
2293       iQ = 2;
2294       // add quadratic links to the helper
2295       for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2296       {
2297         const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
2298         int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
2299         for ( int iN = 0; iN < nbN; iN += iQ )
2300           helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
2301       }
2302       helper.SetIsQuadratic( true );
2303     }
2304     else
2305     {
2306       iQ = 1;
2307       helper.SetIsQuadratic( false );
2308     }
2309     vector<const SMDS_MeshNode*> nodes( volTool.GetNodes(),
2310                                         volTool.GetNodes() + elem->NbNodes() );
2311     helper.SetElementsOnShape( true );
2312     if ( splitMethod._baryNode )
2313     {
2314       // make a node at barycenter
2315       volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
2316       SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
2317       nodes.push_back( gcNode );
2318       newNodes.push_back( gcNode );
2319     }
2320     if ( !splitMethod._faceBaryNode.empty() )
2321     {
2322       // make or find baricentric nodes of faces
2323       map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
2324       for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
2325       {
2326         map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
2327           volFace2BaryNode.insert
2328           ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
2329         if ( !f_n->second )
2330         {
2331           volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
2332           newNodes.push_back( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
2333         }
2334         nodes.push_back( iF_n->second = f_n->second );
2335       }
2336     }
2337
2338     // make new volumes
2339     splitVols.resize( splitMethod._nbSplits ); // splits of a volume
2340     const int* volConn = splitMethod._connectivity;
2341     if ( splitMethod._nbCorners == 4 ) // tetra
2342       for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2343         newElems.push_back( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2344                                                                nodes[ volConn[1] ],
2345                                                                nodes[ volConn[2] ],
2346                                                                nodes[ volConn[3] ]));
2347     else // prisms
2348       for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2349         newElems.push_back( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2350                                                                nodes[ volConn[1] ],
2351                                                                nodes[ volConn[2] ],
2352                                                                nodes[ volConn[3] ],
2353                                                                nodes[ volConn[4] ],
2354                                                                nodes[ volConn[5] ]));
2355
2356     ReplaceElemInGroups( elem, splitVols, GetMeshDS() );
2357
2358     // Split faces on sides of the split volume
2359
2360     const SMDS_MeshNode** volNodes = volTool.GetNodes();
2361     for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2362     {
2363       const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
2364       if ( nbNodes < 4 ) continue;
2365
2366       // find an existing face
2367       vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
2368                                            volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
2369       while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
2370                                                                        /*noMedium=*/false))
2371       {
2372         // make triangles
2373         helper.SetElementsOnShape( false );
2374         vector< const SMDS_MeshElement* > triangles;
2375
2376         // find submesh to add new triangles in
2377         if ( !fSubMesh || !fSubMesh->Contains( face ))
2378         {
2379           int shapeID = FindShape( face );
2380           fSubMesh = GetMeshDS()->MeshElements( shapeID );
2381         }
2382         map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
2383         if ( iF_n != splitMethod._faceBaryNode.end() )
2384         {
2385           const SMDS_MeshNode *baryNode = iF_n->second;
2386           for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
2387           {
2388             const SMDS_MeshNode* n1 = fNodes[iN];
2389             const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
2390             const SMDS_MeshNode *n3 = baryNode;
2391             if ( !volTool.IsFaceExternal( iF ))
2392               swap( n2, n3 );
2393             triangles.push_back( helper.AddFace( n1,n2,n3 ));
2394           }
2395           if ( fSubMesh ) // update position of the bary node on geometry
2396           {
2397             if ( subMesh )
2398               subMesh->RemoveNode( baryNode );
2399             GetMeshDS()->SetNodeOnFace( baryNode, fSubMesh->GetID() );
2400             const TopoDS_Shape& s = GetMeshDS()->IndexToShape( fSubMesh->GetID() );
2401             if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE )
2402             {
2403               fHelper.SetSubShape( s );
2404               gp_XY uv( 1e100, 1e100 );
2405               double distXYZ[4];
2406               if ( !fHelper.CheckNodeUV( TopoDS::Face( s ), baryNode,
2407                                          uv, /*tol=*/1e-7, /*force=*/true, distXYZ ) &&
2408                    uv.X() < 1e100 )
2409               {
2410                 // node is too far from the surface
2411                 GetMeshDS()->MoveNode( baryNode, distXYZ[1], distXYZ[2], distXYZ[3] );
2412                 const_cast<SMDS_MeshNode*>( baryNode )->SetPosition
2413                   ( SMDS_PositionPtr( new SMDS_FacePosition( uv.X(), uv.Y() )));
2414               }
2415             }
2416           }
2417         }
2418         else
2419         {
2420           // among possible triangles create ones described by split method
2421           const int* nInd = volTool.GetFaceNodesIndices( iF );
2422           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2423           int iCom = 0; // common node of triangle faces to split into
2424           list< TTriangleFacet > facets;
2425           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
2426           {
2427             TTriangleFacet t012( nInd[ iQ * ( iCom                )],
2428                                  nInd[ iQ * ( (iCom+1)%nbNodes )],
2429                                  nInd[ iQ * ( (iCom+2)%nbNodes )]);
2430             TTriangleFacet t023( nInd[ iQ * ( iCom                )],
2431                                  nInd[ iQ * ( (iCom+2)%nbNodes )],
2432                                  nInd[ iQ * ( (iCom+3)%nbNodes )]);
2433             if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
2434             {
2435               facets.push_back( t012 );
2436               facets.push_back( t023 );
2437               for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
2438                 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom             )],
2439                                                   nInd[ iQ * ((iLast-1)%nbNodes )],
2440                                                   nInd[ iQ * ((iLast  )%nbNodes )]));
2441               break;
2442             }
2443           }
2444           list< TTriangleFacet >::iterator facet = facets.begin();
2445           if ( facet == facets.end() )
2446             break;
2447           for ( ; facet != facets.end(); ++facet )
2448           {
2449             if ( !volTool.IsFaceExternal( iF ))
2450               swap( facet->_n2, facet->_n3 );
2451             triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
2452                                                  volNodes[ facet->_n2 ],
2453                                                  volNodes[ facet->_n3 ]));
2454           }
2455         }
2456         for ( size_t i = 0; i < triangles.size(); ++i )
2457         {
2458           if ( !triangles[ i ]) continue;
2459           if ( fSubMesh )
2460             fSubMesh->AddElement( triangles[ i ]);
2461           newElems.push_back( triangles[ i ]);
2462         }
2463         ReplaceElemInGroups( face, triangles, GetMeshDS() );
2464         GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2465
2466       } // while a face based on facet nodes exists
2467     } // loop on volume faces to split them into triangles
2468
2469     GetMeshDS()->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2470
2471     if ( geomType == SMDSEntity_TriQuad_Hexa )
2472     {
2473       // remove medium nodes that could become free
2474       for ( int i = 20; i < volTool.NbNodes(); ++i )
2475         if ( volNodes[i]->NbInverseElements() == 0 )
2476           GetMeshDS()->RemoveNode( volNodes[i] );
2477     }
2478   } // loop on volumes to split
2479
2480   myLastCreatedNodes = newNodes;
2481   myLastCreatedElems = newElems;
2482 }
2483
2484 //=======================================================================
2485 //function : GetHexaFacetsToSplit
2486 //purpose  : For hexahedra that will be split into prisms, finds facets to
2487 //           split into triangles. Only hexahedra adjacent to the one closest
2488 //           to theFacetNormal.Location() are returned.
2489 //param [in,out] theHexas - the hexahedra
2490 //param [in]     theFacetNormal - facet normal
2491 //param [out]    theFacets - the hexahedra and found facet IDs
2492 //=======================================================================
2493
2494 void SMESH_MeshEditor::GetHexaFacetsToSplit( TIDSortedElemSet& theHexas,
2495                                              const gp_Ax1&     theFacetNormal,
2496                                              TFacetOfElem &    theFacets)
2497 {
2498 #define THIS_METHOD "SMESH_MeshEditor::GetHexaFacetsToSplit(): "
2499
2500   // Find a hexa closest to the location of theFacetNormal
2501
2502   const SMDS_MeshElement* startHex;
2503   {
2504     // get SMDS_ElemIteratorPtr on theHexas
2505     typedef const SMDS_MeshElement*                                      TValue;
2506     typedef TIDSortedElemSet::iterator                                   TSetIterator;
2507     typedef SMDS::SimpleAccessor<TValue,TSetIterator>                    TAccesor;
2508     typedef SMDS_MeshElement::GeomFilter                                 TFilter;
2509     typedef SMDS_SetIterator < TValue, TSetIterator, TAccesor, TFilter > TElemSetIter;
2510     SMDS_ElemIteratorPtr elemIt = SMDS_ElemIteratorPtr
2511       ( new TElemSetIter( theHexas.begin(),
2512                           theHexas.end(),
2513                           SMDS_MeshElement::GeomFilter( SMDSGeom_HEXA )));
2514
2515     SMESH_ElementSearcher* searcher =
2516       SMESH_MeshAlgos::GetElementSearcher( *myMesh->GetMeshDS(), elemIt );
2517
2518     startHex = searcher->FindClosestTo( theFacetNormal.Location(), SMDSAbs_Volume );
2519
2520     delete searcher;
2521
2522     if ( !startHex )
2523       throw SALOME_Exception( THIS_METHOD "startHex not found");
2524   }
2525
2526   // Select a facet of startHex by theFacetNormal
2527
2528   SMDS_VolumeTool vTool( startHex );
2529   double norm[3], dot, maxDot = 0;
2530   int facetID = -1;
2531   for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2532     if ( vTool.GetFaceNormal( iF, norm[0], norm[1], norm[2] ))
2533     {
2534       dot = Abs( theFacetNormal.Direction().Dot( gp_Dir( norm[0], norm[1], norm[2] )));
2535       if ( dot > maxDot )
2536       {
2537         facetID = iF;
2538         maxDot = dot;
2539       }
2540     }
2541   if ( facetID < 0 )
2542     throw SALOME_Exception( THIS_METHOD "facet of startHex not found");
2543
2544   // Fill theFacets starting from facetID of startHex
2545
2546   // facets used for searching of volumes adjacent to already treated ones
2547   typedef pair< TFacetOfElem::iterator, int > TElemFacets;
2548   typedef map< TVolumeFaceKey, TElemFacets  > TFacetMap;
2549   TFacetMap facetsToCheck;
2550
2551   set<const SMDS_MeshNode*> facetNodes;
2552   const SMDS_MeshElement*   curHex;
2553
2554   const bool allHex = ((int) theHexas.size() == myMesh->NbHexas() );
2555
2556   while ( startHex )
2557   {
2558     // move in two directions from startHex via facetID
2559     for ( int is2nd = 0; is2nd < 2; ++is2nd )
2560     {
2561       curHex       = startHex;
2562       int curFacet = facetID;
2563       if ( is2nd ) // do not treat startHex twice
2564       {
2565         vTool.Set( curHex );
2566         if ( vTool.IsFreeFace( curFacet, &curHex ))
2567         {
2568           curHex = 0;
2569         }
2570         else
2571         {
2572           vTool.GetFaceNodes( curFacet, facetNodes );
2573           vTool.Set( curHex );
2574           curFacet = vTool.GetFaceIndex( facetNodes );
2575         }
2576       }
2577       while ( curHex )
2578       {
2579         // store a facet to split
2580         if ( curHex->GetGeomType() != SMDSGeom_HEXA )
2581         {
2582           theFacets.insert( make_pair( curHex, -1 ));
2583           break;
2584         }
2585         if ( !allHex && !theHexas.count( curHex ))
2586           break;
2587
2588         pair< TFacetOfElem::iterator, bool > facetIt2isNew =
2589           theFacets.insert( make_pair( curHex, curFacet ));
2590         if ( !facetIt2isNew.second )
2591           break;
2592
2593         // remember not-to-split facets in facetsToCheck
2594         int oppFacet = vTool.GetOppFaceIndexOfHex( curFacet );
2595         for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2596         {
2597           if ( iF == curFacet && iF == oppFacet )
2598             continue;
2599           TVolumeFaceKey facetKey ( vTool, iF );
2600           TElemFacets    elemFacet( facetIt2isNew.first, iF );
2601           pair< TFacetMap::iterator, bool > it2isnew =
2602             facetsToCheck.insert( make_pair( facetKey, elemFacet ));
2603           if ( !it2isnew.second )
2604             facetsToCheck.erase( it2isnew.first ); // adjacent hex already checked
2605         }
2606         // pass to a volume adjacent via oppFacet
2607         if ( vTool.IsFreeFace( oppFacet, &curHex ))
2608         {
2609           curHex = 0;
2610         }
2611         else
2612         {
2613           // get a new curFacet
2614           vTool.GetFaceNodes( oppFacet, facetNodes );
2615           vTool.Set( curHex );
2616           curFacet = vTool.GetFaceIndex( facetNodes, /*hint=*/curFacet );
2617         }
2618       }
2619     } // move in two directions from startHex via facetID
2620
2621     // Find a new startHex by facetsToCheck
2622
2623     startHex = 0;
2624     facetID  = -1;
2625     TFacetMap::iterator fIt = facetsToCheck.begin();
2626     while ( !startHex && fIt != facetsToCheck.end() )
2627     {
2628       const TElemFacets&  elemFacets = fIt->second;
2629       const SMDS_MeshElement*    hex = elemFacets.first->first;
2630       int                 splitFacet = elemFacets.first->second;
2631       int               lateralFacet = elemFacets.second;
2632       facetsToCheck.erase( fIt );
2633       fIt = facetsToCheck.begin();
2634
2635       vTool.Set( hex );
2636       if ( vTool.IsFreeFace( lateralFacet, &curHex ) || 
2637            curHex->GetGeomType() != SMDSGeom_HEXA )
2638         continue;
2639       if ( !allHex && !theHexas.count( curHex ))
2640         continue;
2641
2642       startHex = curHex;
2643
2644       // find a facet of startHex to split
2645
2646       set<const SMDS_MeshNode*> lateralNodes;
2647       vTool.GetFaceNodes( lateralFacet, lateralNodes );
2648       vTool.GetFaceNodes( splitFacet,   facetNodes );
2649       int oppLateralFacet = vTool.GetOppFaceIndexOfHex( lateralFacet );
2650       vTool.Set( startHex );
2651       lateralFacet = vTool.GetFaceIndex( lateralNodes, oppLateralFacet );
2652
2653       // look for a facet of startHex having common nodes with facetNodes
2654       // but not lateralFacet
2655       for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2656       {
2657         if ( iF == lateralFacet )
2658           continue;
2659         int nbCommonNodes = 0;
2660         const SMDS_MeshNode** nn = vTool.GetFaceNodes( iF );
2661         for ( int iN = 0, nbN = vTool.NbFaceNodes( iF ); iN < nbN; ++iN )
2662           nbCommonNodes += facetNodes.count( nn[ iN ]);
2663
2664         if ( nbCommonNodes >= 2 )
2665         {
2666           facetID = iF;
2667           break;
2668         }
2669       }
2670       if ( facetID < 0 )
2671         throw SALOME_Exception( THIS_METHOD "facet of a new startHex not found");
2672     }
2673   } //   while ( startHex )
2674
2675   return;
2676 }
2677
2678 namespace
2679 {
2680   //================================================================================
2681   /*!
2682    * \brief Selects nodes of several elements according to a given interlace
2683    *  \param [in] srcNodes - nodes to select from
2684    *  \param [out] tgtNodesVec - array of nodes of several elements to fill in
2685    *  \param [in] interlace - indices of nodes for all elements
2686    *  \param [in] nbElems - nb of elements
2687    *  \param [in] nbNodes - nb of nodes in each element
2688    *  \param [in] mesh - the mesh
2689    *  \param [out] elemQueue - a list to push elements found by the selected nodes
2690    *  \param [in] type - type of elements to look for
2691    */
2692   //================================================================================
2693
2694   void selectNodes( const vector< const SMDS_MeshNode* >& srcNodes,
2695                     vector< const SMDS_MeshNode* >*       tgtNodesVec,
2696                     const int*                            interlace,
2697                     const int                             nbElems,
2698                     const int                             nbNodes,
2699                     SMESHDS_Mesh*                         mesh = 0,
2700                     list< const SMDS_MeshElement* >*      elemQueue=0,
2701                     SMDSAbs_ElementType                   type=SMDSAbs_All)
2702   {
2703     for ( int iE = 0; iE < nbElems; ++iE )
2704     {
2705       vector< const SMDS_MeshNode* >& elemNodes = tgtNodesVec[iE];
2706       const int*                         select = & interlace[iE*nbNodes];
2707       elemNodes.resize( nbNodes );
2708       for ( int iN = 0; iN < nbNodes; ++iN )
2709         elemNodes[iN] = srcNodes[ select[ iN ]];
2710     }
2711     const SMDS_MeshElement* e;
2712     if ( elemQueue )
2713       for ( int iE = 0; iE < nbElems; ++iE )
2714         if (( e = mesh->FindElement( tgtNodesVec[iE], type, /*noMedium=*/false)))
2715           elemQueue->push_back( e );
2716   }
2717 }
2718
2719 //=======================================================================
2720 /*
2721  * Split bi-quadratic elements into linear ones without creation of additional nodes
2722  *   - bi-quadratic triangle will be split into 3 linear quadrangles;
2723  *   - bi-quadratic quadrangle will be split into 4 linear quadrangles;
2724  *   - tri-quadratic hexahedron will be split into 8 linear hexahedra;
2725  *   Quadratic elements of lower dimension  adjacent to the split bi-quadratic element
2726  *   will be split in order to keep the mesh conformal.
2727  *  \param elems - elements to split
2728  */
2729 //=======================================================================
2730
2731 void SMESH_MeshEditor::SplitBiQuadraticIntoLinear(TIDSortedElemSet& theElems)
2732 {
2733   vector< const SMDS_MeshNode* > elemNodes(27), subNodes[12], splitNodes[8];
2734   vector<const SMDS_MeshElement* > splitElems;
2735   list< const SMDS_MeshElement* > elemQueue;
2736   list< const SMDS_MeshElement* >::iterator elemIt;
2737
2738   SMESHDS_Mesh * mesh = GetMeshDS();
2739   ElemFeatures *elemType, hexaType(SMDSAbs_Volume), quadType(SMDSAbs_Face), segType(SMDSAbs_Edge);
2740   int nbElems, nbNodes;
2741
2742   TIDSortedElemSet::iterator elemSetIt = theElems.begin();
2743   for ( ; elemSetIt != theElems.end(); ++elemSetIt )
2744   {
2745     elemQueue.clear();
2746     elemQueue.push_back( *elemSetIt );
2747     for ( elemIt = elemQueue.begin(); elemIt != elemQueue.end(); ++elemIt )
2748     {
2749       const SMDS_MeshElement* elem = *elemIt;
2750       switch( elem->GetEntityType() )
2751       {
2752       case SMDSEntity_TriQuad_Hexa: // HEX27
2753       {
2754         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2755         nbElems  = nbNodes = 8;
2756         elemType = & hexaType;
2757
2758         // get nodes for new elements
2759         static int vInd[8][8] = {{ 0,8,20,11,   16,21,26,24 },
2760                                  { 1,9,20,8,    17,22,26,21 },
2761                                  { 2,10,20,9,   18,23,26,22 },
2762                                  { 3,11,20,10,  19,24,26,23 },
2763                                  { 16,21,26,24, 4,12,25,15  },
2764                                  { 17,22,26,21, 5,13,25,12  },
2765                                  { 18,23,26,22, 6,14,25,13  },
2766                                  { 19,24,26,23, 7,15,25,14  }};
2767         selectNodes( elemNodes, & splitNodes[0], &vInd[0][0], nbElems, nbNodes );
2768
2769         // add boundary faces to elemQueue
2770         static int fInd[6][9] = {{ 0,1,2,3, 8,9,10,11,   20 },
2771                                  { 4,5,6,7, 12,13,14,15, 25 },
2772                                  { 0,1,5,4, 8,17,12,16,  21 },
2773                                  { 1,2,6,5, 9,18,13,17,  22 },
2774                                  { 2,3,7,6, 10,19,14,18, 23 },
2775                                  { 3,0,4,7, 11,16,15,19, 24 }};
2776         selectNodes( elemNodes, & subNodes[0], &fInd[0][0], 6,9, mesh, &elemQueue, SMDSAbs_Face );
2777
2778         // add boundary segments to elemQueue
2779         static int eInd[12][3] = {{ 0,1,8 }, { 1,2,9 }, { 2,3,10 }, { 3,0,11 },
2780                                   { 4,5,12}, { 5,6,13}, { 6,7,14 }, { 7,4,15 },
2781                                   { 0,4,16}, { 1,5,17}, { 2,6,18 }, { 3,7,19 }};
2782         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 12,3, mesh, &elemQueue, SMDSAbs_Edge );
2783         break;
2784       }
2785       case SMDSEntity_BiQuad_Triangle: // TRIA7
2786       {
2787         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2788         nbElems = 3;
2789         nbNodes = 4;
2790         elemType = & quadType;
2791
2792         // get nodes for new elements
2793         static int fInd[3][4] = {{ 0,3,6,5 }, { 1,4,6,3 }, { 2,5,6,4 }};
2794         selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2795
2796         // add boundary segments to elemQueue
2797         static int eInd[3][3] = {{ 0,1,3 }, { 1,2,4 }, { 2,0,5 }};
2798         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 3,3, mesh, &elemQueue, SMDSAbs_Edge );
2799         break;
2800       }
2801       case SMDSEntity_BiQuad_Quadrangle: // QUAD9
2802       {
2803         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2804         nbElems = 4;
2805         nbNodes = 4;
2806         elemType = & quadType;
2807
2808         // get nodes for new elements
2809         static int fInd[4][4] = {{ 0,4,8,7 }, { 1,5,8,4 }, { 2,6,8,5 }, { 3,7,8,6 }};
2810         selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2811
2812         // add boundary segments to elemQueue
2813         static int eInd[4][3] = {{ 0,1,4 }, { 1,2,5 }, { 2,3,6 }, { 3,0,7 }};
2814         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 4,3, mesh, &elemQueue, SMDSAbs_Edge );
2815         break;
2816       }
2817       case SMDSEntity_Quad_Edge:
2818       {
2819         if ( elemIt == elemQueue.begin() )
2820           continue; // an elem is in theElems
2821         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2822         nbElems = 2;
2823         nbNodes = 2;
2824         elemType = & segType;
2825
2826         // get nodes for new elements
2827         static int eInd[2][2] = {{ 0,2 }, { 2,1 }};
2828         selectNodes( elemNodes, & splitNodes[0], &eInd[0][0], nbElems, nbNodes );
2829         break;
2830       }
2831       default: continue;
2832       } // switch( elem->GetEntityType() )
2833
2834       // Create new elements
2835
2836       SMESHDS_SubMesh* subMesh = mesh->MeshElements( elem->getshapeId() );
2837
2838       splitElems.clear();
2839
2840       //elemType->SetID( elem->GetID() ); // create an elem with the same ID as a removed one
2841       mesh->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2842       //splitElems.push_back( AddElement( splitNodes[ 0 ], *elemType ));
2843       //elemType->SetID( -1 );
2844
2845       for ( int iE = 0; iE < nbElems; ++iE )
2846         splitElems.push_back( AddElement( splitNodes[ iE ], *elemType ));
2847
2848
2849       ReplaceElemInGroups( elem, splitElems, mesh );
2850
2851       if ( subMesh )
2852         for ( size_t i = 0; i < splitElems.size(); ++i )
2853           subMesh->AddElement( splitElems[i] );
2854     }
2855   }
2856 }
2857
2858 //=======================================================================
2859 //function : AddToSameGroups
2860 //purpose  : add elemToAdd to the groups the elemInGroups belongs to
2861 //=======================================================================
2862
2863 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
2864                                         const SMDS_MeshElement* elemInGroups,
2865                                         SMESHDS_Mesh *          aMesh)
2866 {
2867   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2868   if (!groups.empty()) {
2869     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2870     for ( ; grIt != groups.end(); grIt++ ) {
2871       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2872       if ( group && group->Contains( elemInGroups ))
2873         group->SMDSGroup().Add( elemToAdd );
2874     }
2875   }
2876 }
2877
2878
2879 //=======================================================================
2880 //function : RemoveElemFromGroups
2881 //purpose  : Remove removeelem to the groups the elemInGroups belongs to
2882 //=======================================================================
2883 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2884                                              SMESHDS_Mesh *          aMesh)
2885 {
2886   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2887   if (!groups.empty())
2888   {
2889     set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2890     for (; GrIt != groups.end(); GrIt++)
2891     {
2892       SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2893       if (!grp || grp->IsEmpty()) continue;
2894       grp->SMDSGroup().Remove(removeelem);
2895     }
2896   }
2897 }
2898
2899 //================================================================================
2900 /*!
2901  * \brief Replace elemToRm by elemToAdd in the all groups
2902  */
2903 //================================================================================
2904
2905 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2906                                             const SMDS_MeshElement* elemToAdd,
2907                                             SMESHDS_Mesh *          aMesh)
2908 {
2909   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2910   if (!groups.empty()) {
2911     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2912     for ( ; grIt != groups.end(); grIt++ ) {
2913       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2914       if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2915         group->SMDSGroup().Add( elemToAdd );
2916     }
2917   }
2918 }
2919
2920 //================================================================================
2921 /*!
2922  * \brief Replace elemToRm by elemToAdd in the all groups
2923  */
2924 //================================================================================
2925
2926 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement*                elemToRm,
2927                                             const vector<const SMDS_MeshElement*>& elemToAdd,
2928                                             SMESHDS_Mesh *                         aMesh)
2929 {
2930   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2931   if (!groups.empty())
2932   {
2933     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2934     for ( ; grIt != groups.end(); grIt++ ) {
2935       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2936       if ( group && group->SMDSGroup().Remove( elemToRm ) )
2937         for ( size_t i = 0; i < elemToAdd.size(); ++i )
2938           group->SMDSGroup().Add( elemToAdd[ i ] );
2939     }
2940   }
2941 }
2942
2943 //=======================================================================
2944 //function : QuadToTri
2945 //purpose  : Cut quadrangles into triangles.
2946 //           theCrit is used to select a diagonal to cut
2947 //=======================================================================
2948
2949 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2950                                   const bool         the13Diag)
2951 {
2952   ClearLastCreated();
2953   myLastCreatedElems.reserve( theElems.size() * 2 );
2954
2955   SMESHDS_Mesh *       aMesh = GetMeshDS();
2956   Handle(Geom_Surface) surface;
2957   SMESH_MesherHelper   helper( *GetMesh() );
2958
2959   TIDSortedElemSet::iterator itElem;
2960   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
2961   {
2962     const SMDS_MeshElement* elem = *itElem;
2963     if ( !elem || elem->GetGeomType() != SMDSGeom_QUADRANGLE )
2964       continue;
2965
2966     if ( elem->NbNodes() == 4 ) {
2967       // retrieve element nodes
2968       const SMDS_MeshNode* aNodes [4];
2969       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2970       int i = 0;
2971       while ( itN->more() )
2972         aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2973
2974       int aShapeId = FindShape( elem );
2975       const SMDS_MeshElement* newElem1 = 0;
2976       const SMDS_MeshElement* newElem2 = 0;
2977       if ( the13Diag ) {
2978         newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2979         newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2980       }
2981       else {
2982         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2983         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2984       }
2985       myLastCreatedElems.push_back(newElem1);
2986       myLastCreatedElems.push_back(newElem2);
2987       // put a new triangle on the same shape and add to the same groups
2988       if ( aShapeId )
2989       {
2990         aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2991         aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2992       }
2993       AddToSameGroups( newElem1, elem, aMesh );
2994       AddToSameGroups( newElem2, elem, aMesh );
2995       aMesh->RemoveElement( elem );
2996     }
2997
2998     // Quadratic quadrangle
2999
3000     else if ( elem->NbNodes() >= 8 )
3001     {
3002       // get surface elem is on
3003       int aShapeId = FindShape( elem );
3004       if ( aShapeId != helper.GetSubShapeID() ) {
3005         surface.Nullify();
3006         TopoDS_Shape shape;
3007         if ( aShapeId > 0 )
3008           shape = aMesh->IndexToShape( aShapeId );
3009         if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
3010           TopoDS_Face face = TopoDS::Face( shape );
3011           surface = BRep_Tool::Surface( face );
3012           if ( !surface.IsNull() )
3013             helper.SetSubShape( shape );
3014         }
3015       }
3016
3017       const SMDS_MeshNode* aNodes [9]; aNodes[8] = 0;
3018       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3019       for ( int i = 0; itN->more(); ++i )
3020         aNodes[ i ] = static_cast<const SMDS_MeshNode*>( itN->next() );
3021
3022       const SMDS_MeshNode* centrNode = aNodes[8];
3023       if ( centrNode == 0 )
3024       {
3025         centrNode = helper.GetCentralNode( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3026                                            aNodes[4], aNodes[5], aNodes[6], aNodes[7],
3027                                            surface.IsNull() );
3028         myLastCreatedNodes.push_back(centrNode);
3029       }
3030
3031       // create a new element
3032       const SMDS_MeshElement* newElem1 = 0;
3033       const SMDS_MeshElement* newElem2 = 0;
3034       if ( the13Diag ) {
3035         newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
3036                                   aNodes[6], aNodes[7], centrNode );
3037         newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
3038                                   centrNode, aNodes[4], aNodes[5] );
3039       }
3040       else {
3041         newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
3042                                   aNodes[7], aNodes[4], centrNode );
3043         newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
3044                                   centrNode, aNodes[5], aNodes[6] );
3045       }
3046       myLastCreatedElems.push_back(newElem1);
3047       myLastCreatedElems.push_back(newElem2);
3048       // put a new triangle on the same shape and add to the same groups
3049       if ( aShapeId )
3050       {
3051         aMesh->SetMeshElementOnShape( newElem1, aShapeId );
3052         aMesh->SetMeshElementOnShape( newElem2, aShapeId );
3053       }
3054       AddToSameGroups( newElem1, elem, aMesh );
3055       AddToSameGroups( newElem2, elem, aMesh );
3056       aMesh->RemoveElement( elem );
3057     }
3058   }
3059
3060   return true;
3061 }
3062
3063 //=======================================================================
3064 //function : getAngle
3065 //purpose  :
3066 //=======================================================================
3067
3068 double getAngle(const SMDS_MeshElement * tr1,
3069                 const SMDS_MeshElement * tr2,
3070                 const SMDS_MeshNode *    n1,
3071                 const SMDS_MeshNode *    n2)
3072 {
3073   double angle = 2. * M_PI; // bad angle
3074
3075   // get normals
3076   SMESH::Controls::TSequenceOfXYZ P1, P2;
3077   if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
3078        !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
3079     return angle;
3080   gp_Vec N1,N2;
3081   if(!tr1->IsQuadratic())
3082     N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
3083   else
3084     N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
3085   if ( N1.SquareMagnitude() <= gp::Resolution() )
3086     return angle;
3087   if(!tr2->IsQuadratic())
3088     N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
3089   else
3090     N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
3091   if ( N2.SquareMagnitude() <= gp::Resolution() )
3092     return angle;
3093
3094   // find the first diagonal node n1 in the triangles:
3095   // take in account a diagonal link orientation
3096   const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
3097   for ( int t = 0; t < 2; t++ ) {
3098     SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
3099     int i = 0, iDiag = -1;
3100     while ( it->more()) {
3101       const SMDS_MeshElement *n = it->next();
3102       if ( n == n1 || n == n2 ) {
3103         if ( iDiag < 0)
3104           iDiag = i;
3105         else {
3106           if ( i - iDiag == 1 )
3107             nFirst[ t ] = ( n == n1 ? n2 : n1 );
3108           else
3109             nFirst[ t ] = n;
3110           break;
3111         }
3112       }
3113       i++;
3114     }
3115   }
3116   if ( nFirst[ 0 ] == nFirst[ 1 ] )
3117     N2.Reverse();
3118
3119   angle = N1.Angle( N2 );
3120   //SCRUTE( angle );
3121   return angle;
3122 }
3123
3124 // =================================================
3125 // class generating a unique ID for a pair of nodes
3126 // and able to return nodes by that ID
3127 // =================================================
3128 class LinkID_Gen {
3129 public:
3130
3131   LinkID_Gen( const SMESHDS_Mesh* theMesh )
3132     :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
3133   {}
3134
3135   long GetLinkID (const SMDS_MeshNode * n1,
3136                   const SMDS_MeshNode * n2) const
3137   {
3138     return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
3139   }
3140
3141   bool GetNodes (const long             theLinkID,
3142                  const SMDS_MeshNode* & theNode1,
3143                  const SMDS_MeshNode* & theNode2) const
3144   {
3145     theNode1 = myMesh->FindNode( theLinkID / myMaxID );
3146     if ( !theNode1 ) return false;
3147     theNode2 = myMesh->FindNode( theLinkID % myMaxID );
3148     if ( !theNode2 ) return false;
3149     return true;
3150   }
3151
3152 private:
3153   LinkID_Gen();
3154   const SMESHDS_Mesh* myMesh;
3155   long                myMaxID;
3156 };
3157
3158
3159 //=======================================================================
3160 //function : TriToQuad
3161 //purpose  : Fuse neighbour triangles into quadrangles.
3162 //           theCrit is used to select a neighbour to fuse with.
3163 //           theMaxAngle is a max angle between element normals at which
3164 //           fusion is still performed.
3165 //=======================================================================
3166
3167 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet &                   theElems,
3168                                   SMESH::Controls::NumericalFunctorPtr theCrit,
3169                                   const double                         theMaxAngle)
3170 {
3171   ClearLastCreated();
3172   myLastCreatedElems.reserve( theElems.size() / 2 );
3173
3174   if ( !theCrit.get() )
3175     return false;
3176
3177   SMESHDS_Mesh * aMesh = GetMeshDS();
3178
3179   // Prepare data for algo: build
3180   // 1. map of elements with their linkIDs
3181   // 2. map of linkIDs with their elements
3182
3183   map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
3184   map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
3185   map< const SMDS_MeshElement*, set< SMESH_TLink > >  mapEl_setLi;
3186   map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
3187
3188   TIDSortedElemSet::iterator itElem;
3189   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
3190   {
3191     const SMDS_MeshElement* elem = *itElem;
3192     if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
3193     bool IsTria = ( elem->NbCornerNodes()==3 );
3194     if (!IsTria) continue;
3195
3196     // retrieve element nodes
3197     const SMDS_MeshNode* aNodes [4];
3198     SMDS_NodeIteratorPtr itN = elem->nodeIterator();
3199     int i = 0;
3200     while ( i < 3 )
3201       aNodes[ i++ ] = itN->next();
3202     aNodes[ 3 ] = aNodes[ 0 ];
3203
3204     // fill maps
3205     for ( i = 0; i < 3; i++ ) {
3206       SMESH_TLink link( aNodes[i], aNodes[i+1] );
3207       // check if elements sharing a link can be fused
3208       itLE = mapLi_listEl.find( link );
3209       if ( itLE != mapLi_listEl.end() ) {
3210         if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
3211           continue;
3212         const SMDS_MeshElement* elem2 = (*itLE).second.front();
3213         //if ( FindShape( elem ) != FindShape( elem2 ))
3214         //  continue; // do not fuse triangles laying on different shapes
3215         if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
3216           continue; // avoid making badly shaped quads
3217         (*itLE).second.push_back( elem );
3218       }
3219       else {
3220         mapLi_listEl[ link ].push_back( elem );
3221       }
3222       mapEl_setLi [ elem ].insert( link );
3223     }
3224   }
3225   // Clean the maps from the links shared by a sole element, ie
3226   // links to which only one element is bound in mapLi_listEl
3227
3228   for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
3229     int nbElems = (*itLE).second.size();
3230     if ( nbElems < 2  ) {
3231       const SMDS_MeshElement* elem = (*itLE).second.front();
3232       SMESH_TLink link = (*itLE).first;
3233       mapEl_setLi[ elem ].erase( link );
3234       if ( mapEl_setLi[ elem ].empty() )
3235         mapEl_setLi.erase( elem );
3236     }
3237   }
3238
3239   // Algo: fuse triangles into quadrangles
3240
3241   while ( ! mapEl_setLi.empty() ) {
3242     // Look for the start element:
3243     // the element having the least nb of shared links
3244     const SMDS_MeshElement* startElem = 0;
3245     int minNbLinks = 4;
3246     for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
3247       int nbLinks = (*itEL).second.size();
3248       if ( nbLinks < minNbLinks ) {
3249         startElem = (*itEL).first;
3250         minNbLinks = nbLinks;
3251         if ( minNbLinks == 1 )
3252           break;
3253       }
3254     }
3255
3256     // search elements to fuse starting from startElem or links of elements
3257     // fused earlyer - startLinks
3258     list< SMESH_TLink > startLinks;
3259     while ( startElem || !startLinks.empty() ) {
3260       while ( !startElem && !startLinks.empty() ) {
3261         // Get an element to start, by a link
3262         SMESH_TLink linkId = startLinks.front();
3263         startLinks.pop_front();
3264         itLE = mapLi_listEl.find( linkId );
3265         if ( itLE != mapLi_listEl.end() ) {
3266           list< const SMDS_MeshElement* > & listElem = (*itLE).second;
3267           list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
3268           for ( ; itE != listElem.end() ; itE++ )
3269             if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
3270               startElem = (*itE);
3271           mapLi_listEl.erase( itLE );
3272         }
3273       }
3274
3275       if ( startElem ) {
3276         // Get candidates to be fused
3277         const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
3278         const SMESH_TLink *link12 = 0, *link13 = 0;
3279         startElem = 0;
3280         ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
3281         set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
3282         ASSERT( !setLi.empty() );
3283         set< SMESH_TLink >::iterator itLi;
3284         for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
3285         {
3286           const SMESH_TLink & link = (*itLi);
3287           itLE = mapLi_listEl.find( link );
3288           if ( itLE == mapLi_listEl.end() )
3289             continue;
3290
3291           const SMDS_MeshElement* elem = (*itLE).second.front();
3292           if ( elem == tr1 )
3293             elem = (*itLE).second.back();
3294           mapLi_listEl.erase( itLE );
3295           if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
3296             continue;
3297           if ( tr2 ) {
3298             tr3 = elem;
3299             link13 = &link;
3300           }
3301           else {
3302             tr2 = elem;
3303             link12 = &link;
3304           }
3305
3306           // add other links of elem to list of links to re-start from
3307           set< SMESH_TLink >& links = mapEl_setLi[ elem ];
3308           set< SMESH_TLink >::iterator it;
3309           for ( it = links.begin(); it != links.end(); it++ ) {
3310             const SMESH_TLink& link2 = (*it);
3311             if ( link2 != link )
3312               startLinks.push_back( link2 );
3313           }
3314         }
3315
3316         // Get nodes of possible quadrangles
3317         const SMDS_MeshNode *n12 [4], *n13 [4];
3318         bool Ok12 = false, Ok13 = false;
3319         const SMDS_MeshNode *linkNode1, *linkNode2;
3320         if(tr2) {
3321           linkNode1 = link12->first;
3322           linkNode2 = link12->second;
3323           if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
3324             Ok12 = true;
3325         }
3326         if(tr3) {
3327           linkNode1 = link13->first;
3328           linkNode2 = link13->second;
3329           if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
3330             Ok13 = true;
3331         }
3332
3333         // Choose a pair to fuse
3334         if ( Ok12 && Ok13 ) {
3335           SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
3336           SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
3337           double aBadRate12 = getBadRate( &quad12, theCrit );
3338           double aBadRate13 = getBadRate( &quad13, theCrit );
3339           if (  aBadRate13 < aBadRate12 )
3340             Ok12 = false;
3341           else
3342             Ok13 = false;
3343         }
3344
3345         // Make quadrangles
3346         // and remove fused elems and remove links from the maps
3347         mapEl_setLi.erase( tr1 );
3348         if ( Ok12 )
3349         {
3350           mapEl_setLi.erase( tr2 );
3351           mapLi_listEl.erase( *link12 );
3352           if ( tr1->NbNodes() == 3 )
3353           {
3354             const SMDS_MeshElement* newElem = 0;
3355             newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
3356             myLastCreatedElems.push_back(newElem);
3357             AddToSameGroups( newElem, tr1, aMesh );
3358             int aShapeId = tr1->getshapeId();
3359             if ( aShapeId )
3360               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3361             aMesh->RemoveElement( tr1 );
3362             aMesh->RemoveElement( tr2 );
3363           }
3364           else {
3365             vector< const SMDS_MeshNode* > N1;
3366             vector< const SMDS_MeshNode* > N2;
3367             getNodesFromTwoTria(tr1,tr2,N1,N2);
3368             // now we receive following N1 and N2 (using numeration as in image in InverseDiag())
3369             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
3370             // i.e. first nodes from both arrays form a new diagonal
3371             const SMDS_MeshNode* aNodes[8];
3372             aNodes[0] = N1[0];
3373             aNodes[1] = N1[1];
3374             aNodes[2] = N2[0];
3375             aNodes[3] = N2[1];
3376             aNodes[4] = N1[3];
3377             aNodes[5] = N2[5];
3378             aNodes[6] = N2[3];
3379             aNodes[7] = N1[5];
3380             const SMDS_MeshElement* newElem = 0;
3381             if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3382               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3383                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3384             else
3385               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3386                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3387             myLastCreatedElems.push_back(newElem);
3388             AddToSameGroups( newElem, tr1, aMesh );
3389             int aShapeId = tr1->getshapeId();
3390             if ( aShapeId )
3391               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3392             aMesh->RemoveElement( tr1 );
3393             aMesh->RemoveElement( tr2 );
3394             // remove middle node (9)
3395             if ( N1[4]->NbInverseElements() == 0 )
3396               aMesh->RemoveNode( N1[4] );
3397             if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3398               aMesh->RemoveNode( N1[6] );
3399             if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3400               aMesh->RemoveNode( N2[6] );
3401           }
3402         }
3403         else if ( Ok13 )
3404         {
3405           mapEl_setLi.erase( tr3 );
3406           mapLi_listEl.erase( *link13 );
3407           if ( tr1->NbNodes() == 3 ) {
3408             const SMDS_MeshElement* newElem = 0;
3409             newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
3410             myLastCreatedElems.push_back(newElem);
3411             AddToSameGroups( newElem, tr1, aMesh );
3412             int aShapeId = tr1->getshapeId();
3413             if ( aShapeId )
3414               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3415             aMesh->RemoveElement( tr1 );
3416             aMesh->RemoveElement( tr3 );
3417           }
3418           else {
3419             vector< const SMDS_MeshNode* > N1;
3420             vector< const SMDS_MeshNode* > N2;
3421             getNodesFromTwoTria(tr1,tr3,N1,N2);
3422             // now we receive following N1 and N2 (using numeration as above image)
3423             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
3424             // i.e. first nodes from both arrays form a new diagonal
3425             const SMDS_MeshNode* aNodes[8];
3426             aNodes[0] = N1[0];
3427             aNodes[1] = N1[1];
3428             aNodes[2] = N2[0];
3429             aNodes[3] = N2[1];
3430             aNodes[4] = N1[3];
3431             aNodes[5] = N2[5];
3432             aNodes[6] = N2[3];
3433             aNodes[7] = N1[5];
3434             const SMDS_MeshElement* newElem = 0;
3435             if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3436               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3437                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3438             else
3439               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3440                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3441             myLastCreatedElems.push_back(newElem);
3442             AddToSameGroups( newElem, tr1, aMesh );
3443             int aShapeId = tr1->getshapeId();
3444             if ( aShapeId )
3445               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3446             aMesh->RemoveElement( tr1 );
3447             aMesh->RemoveElement( tr3 );
3448             // remove middle node (9)
3449             if ( N1[4]->NbInverseElements() == 0 )
3450               aMesh->RemoveNode( N1[4] );
3451             if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3452               aMesh->RemoveNode( N1[6] );
3453             if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3454               aMesh->RemoveNode( N2[6] );
3455           }
3456         }
3457
3458         // Next element to fuse: the rejected one
3459         if ( tr3 )
3460           startElem = Ok12 ? tr3 : tr2;
3461
3462       } // if ( startElem )
3463     } // while ( startElem || !startLinks.empty() )
3464   } // while ( ! mapEl_setLi.empty() )
3465
3466   return true;
3467 }
3468
3469 //================================================================================
3470 /*!
3471  * \brief Return nodes linked to the given one
3472  * \param theNode - the node
3473  * \param linkedNodes - the found nodes
3474  * \param type - the type of elements to check
3475  *
3476  * Medium nodes are ignored
3477  */
3478 //================================================================================
3479
3480 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
3481                                        TIDSortedElemSet &   linkedNodes,
3482                                        SMDSAbs_ElementType  type )
3483 {
3484   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
3485   while ( elemIt->more() )
3486   {
3487     const SMDS_MeshElement* elem = elemIt->next();
3488     if(elem->GetType() == SMDSAbs_0DElement)
3489       continue;
3490
3491     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
3492     if ( elem->GetType() == SMDSAbs_Volume )
3493     {
3494       SMDS_VolumeTool vol( elem );
3495       while ( nodeIt->more() ) {
3496         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3497         if ( theNode != n && vol.IsLinked( theNode, n ))
3498           linkedNodes.insert( n );
3499       }
3500     }
3501     else
3502     {
3503       for ( int i = 0; nodeIt->more(); ++i ) {
3504         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3505         if ( n == theNode ) {
3506           int iBefore = i - 1;
3507           int iAfter  = i + 1;
3508           if ( elem->IsQuadratic() ) {
3509             int nb = elem->NbNodes() / 2;
3510             iAfter  = SMESH_MesherHelper::WrapIndex( iAfter, nb );
3511             iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
3512           }
3513           linkedNodes.insert( elem->GetNodeWrap( iAfter ));
3514           linkedNodes.insert( elem->GetNodeWrap( iBefore ));
3515         }
3516       }
3517     }
3518   }
3519 }
3520
3521 //=======================================================================
3522 //function : laplacianSmooth
3523 //purpose  : pulls theNode toward the center of surrounding nodes directly
3524 //           connected to that node along an element edge
3525 //=======================================================================
3526
3527 void laplacianSmooth(const SMDS_MeshNode*                 theNode,
3528                      const Handle(Geom_Surface)&          theSurface,
3529                      map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3530 {
3531   // find surrounding nodes
3532
3533   TIDSortedElemSet nodeSet;
3534   SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
3535
3536   // compute new coodrs
3537
3538   double coord[] = { 0., 0., 0. };
3539   TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3540   for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
3541     const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3542     if ( theSurface.IsNull() ) { // smooth in 3D
3543       coord[0] += node->X();
3544       coord[1] += node->Y();
3545       coord[2] += node->Z();
3546     }
3547     else { // smooth in 2D
3548       ASSERT( theUVMap.find( node ) != theUVMap.end() );
3549       gp_XY* uv = theUVMap[ node ];
3550       coord[0] += uv->X();
3551       coord[1] += uv->Y();
3552     }
3553   }
3554   int nbNodes = nodeSet.size();
3555   if ( !nbNodes )
3556     return;
3557   coord[0] /= nbNodes;
3558   coord[1] /= nbNodes;
3559
3560   if ( !theSurface.IsNull() ) {
3561     ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3562     theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3563     gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3564     coord[0] = p3d.X();
3565     coord[1] = p3d.Y();
3566     coord[2] = p3d.Z();
3567   }
3568   else
3569     coord[2] /= nbNodes;
3570
3571   // move node
3572
3573   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3574 }
3575
3576 //=======================================================================
3577 //function : centroidalSmooth
3578 //purpose  : pulls theNode toward the element-area-weighted centroid of the
3579 //           surrounding elements
3580 //=======================================================================
3581
3582 void centroidalSmooth(const SMDS_MeshNode*                 theNode,
3583                       const Handle(Geom_Surface)&          theSurface,
3584                       map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3585 {
3586   gp_XYZ aNewXYZ(0.,0.,0.);
3587   SMESH::Controls::Area anAreaFunc;
3588   double totalArea = 0.;
3589   int nbElems = 0;
3590
3591   // compute new XYZ
3592
3593   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3594   while ( elemIt->more() )
3595   {
3596     const SMDS_MeshElement* elem = elemIt->next();
3597     nbElems++;
3598
3599     gp_XYZ elemCenter(0.,0.,0.);
3600     SMESH::Controls::TSequenceOfXYZ aNodePoints;
3601     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3602     int nn = elem->NbNodes();
3603     if(elem->IsQuadratic()) nn = nn/2;
3604     int i=0;
3605     //while ( itN->more() ) {
3606     while ( i<nn ) {
3607       const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3608       i++;
3609       gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3610       aNodePoints.push_back( aP );
3611       if ( !theSurface.IsNull() ) { // smooth in 2D
3612         ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3613         gp_XY* uv = theUVMap[ aNode ];
3614         aP.SetCoord( uv->X(), uv->Y(), 0. );
3615       }
3616       elemCenter += aP;
3617     }
3618     double elemArea = anAreaFunc.GetValue( aNodePoints );
3619     totalArea += elemArea;
3620     elemCenter /= nn;
3621     aNewXYZ += elemCenter * elemArea;
3622   }
3623   aNewXYZ /= totalArea;
3624   if ( !theSurface.IsNull() ) {
3625     theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3626     aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3627   }
3628
3629   // move node
3630
3631   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3632 }
3633
3634 //=======================================================================
3635 //function : getClosestUV
3636 //purpose  : return UV of closest projection
3637 //=======================================================================
3638
3639 static bool getClosestUV (Extrema_GenExtPS& projector,
3640                           const gp_Pnt&     point,
3641                           gp_XY &           result)
3642 {
3643   projector.Perform( point );
3644   if ( projector.IsDone() ) {
3645     double u = 0, v = 0, minVal = DBL_MAX;
3646     for ( int i = projector.NbExt(); i > 0; i-- )
3647       if ( projector.SquareDistance( i ) < minVal ) {
3648         minVal = projector.SquareDistance( i );
3649         projector.Point( i ).Parameter( u, v );
3650       }
3651     result.SetCoord( u, v );
3652     return true;
3653   }
3654   return false;
3655 }
3656
3657 //=======================================================================
3658 //function : Smooth
3659 //purpose  : Smooth theElements during theNbIterations or until a worst
3660 //           element has aspect ratio <= theTgtAspectRatio.
3661 //           Aspect Ratio varies in range [1.0, inf].
3662 //           If theElements is empty, the whole mesh is smoothed.
3663 //           theFixedNodes contains additionally fixed nodes. Nodes built
3664 //           on edges and boundary nodes are always fixed.
3665 //=======================================================================
3666
3667 void SMESH_MeshEditor::Smooth (TIDSortedElemSet &          theElems,
3668                                set<const SMDS_MeshNode*> & theFixedNodes,
3669                                const SmoothMethod          theSmoothMethod,
3670                                const int                   theNbIterations,
3671                                double                      theTgtAspectRatio,
3672                                const bool                  the2D)
3673 {
3674   ClearLastCreated();
3675
3676   if ( theTgtAspectRatio < 1.0 )
3677     theTgtAspectRatio = 1.0;
3678
3679   const double disttol = 1.e-16;
3680
3681   SMESH::Controls::AspectRatio aQualityFunc;
3682
3683   SMESHDS_Mesh* aMesh = GetMeshDS();
3684
3685   if ( theElems.empty() ) {
3686     // add all faces to theElems
3687     SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3688     while ( fIt->more() ) {
3689       const SMDS_MeshElement* face = fIt->next();
3690       theElems.insert( theElems.end(), face );
3691     }
3692   }
3693   // get all face ids theElems are on
3694   set< int > faceIdSet;
3695   TIDSortedElemSet::iterator itElem;
3696   if ( the2D )
3697     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3698       int fId = FindShape( *itElem );
3699       // check that corresponding submesh exists and a shape is face
3700       if (fId &&
3701           faceIdSet.find( fId ) == faceIdSet.end() &&
3702           aMesh->MeshElements( fId )) {
3703         TopoDS_Shape F = aMesh->IndexToShape( fId );
3704         if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3705           faceIdSet.insert( fId );
3706       }
3707     }
3708   faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3709
3710   // ===============================================
3711   // smooth elements on each TopoDS_Face separately
3712   // ===============================================
3713
3714   SMESH_MesherHelper helper( *GetMesh() );
3715
3716   set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treat 0 fId at the end
3717   for ( ; fId != faceIdSet.rend(); ++fId )
3718   {
3719     // get face surface and submesh
3720     Handle(Geom_Surface) surface;
3721     SMESHDS_SubMesh* faceSubMesh = 0;
3722     TopoDS_Face face;
3723     double fToler2 = 0;
3724     double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3725     bool isUPeriodic = false, isVPeriodic = false;
3726     if ( *fId )
3727     {
3728       face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3729       surface = BRep_Tool::Surface( face );
3730       faceSubMesh = aMesh->MeshElements( *fId );
3731       fToler2 = BRep_Tool::Tolerance( face );
3732       fToler2 *= fToler2 * 10.;
3733       isUPeriodic = surface->IsUPeriodic();
3734       // if ( isUPeriodic )
3735       //   surface->UPeriod();
3736       isVPeriodic = surface->IsVPeriodic();
3737       // if ( isVPeriodic )
3738       //   surface->VPeriod();
3739       surface->Bounds( u1, u2, v1, v2 );
3740       helper.SetSubShape( face );
3741     }
3742     // ---------------------------------------------------------
3743     // for elements on a face, find movable and fixed nodes and
3744     // compute UV for them
3745     // ---------------------------------------------------------
3746     bool checkBoundaryNodes = false;
3747     bool isQuadratic = false;
3748     set<const SMDS_MeshNode*> setMovableNodes;
3749     map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
3750     list< gp_XY > listUV; // uvs the 2 uvMaps refer to
3751     list< const SMDS_MeshElement* > elemsOnFace;
3752
3753     Extrema_GenExtPS projector;
3754     GeomAdaptor_Surface surfAdaptor;
3755     if ( !surface.IsNull() ) {
3756       surfAdaptor.Load( surface );
3757       projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
3758     }
3759     int nbElemOnFace = 0;
3760     itElem = theElems.begin();
3761     // loop on not yet smoothed elements: look for elems on a face
3762     while ( itElem != theElems.end() )
3763     {
3764       if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
3765         break; // all elements found
3766
3767       const SMDS_MeshElement* elem = *itElem;
3768       if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
3769            ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
3770         ++itElem;
3771         continue;
3772       }
3773       elemsOnFace.push_back( elem );
3774       theElems.erase( itElem++ );
3775       nbElemOnFace++;
3776
3777       if ( !isQuadratic )
3778         isQuadratic = elem->IsQuadratic();
3779
3780       // get movable nodes of elem
3781       const SMDS_MeshNode* node;
3782       SMDS_TypeOfPosition posType;
3783       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3784       int nn = 0, nbn =  elem->NbNodes();
3785       if(elem->IsQuadratic())
3786         nbn = nbn/2;
3787       while ( nn++ < nbn ) {
3788         node = static_cast<const SMDS_MeshNode*>( itN->next() );
3789         const SMDS_PositionPtr& pos = node->GetPosition();
3790         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3791         if (posType != SMDS_TOP_EDGE &&
3792             posType != SMDS_TOP_VERTEX &&
3793             theFixedNodes.find( node ) == theFixedNodes.end())
3794         {
3795           // check if all faces around the node are on faceSubMesh
3796           // because a node on edge may be bound to face
3797           bool all = true;
3798           if ( faceSubMesh ) {
3799             SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3800             while ( eIt->more() && all ) {
3801               const SMDS_MeshElement* e = eIt->next();
3802               all = faceSubMesh->Contains( e );
3803             }
3804           }
3805           if ( all )
3806             setMovableNodes.insert( node );
3807           else
3808             checkBoundaryNodes = true;
3809         }
3810         if ( posType == SMDS_TOP_3DSPACE )
3811           checkBoundaryNodes = true;
3812       }
3813
3814       if ( surface.IsNull() )
3815         continue;
3816
3817       // get nodes to check UV
3818       list< const SMDS_MeshNode* > uvCheckNodes;
3819       const SMDS_MeshNode* nodeInFace = 0;
3820       itN = elem->nodesIterator();
3821       nn = 0; nbn =  elem->NbNodes();
3822       if(elem->IsQuadratic())
3823         nbn = nbn/2;
3824       while ( nn++ < nbn ) {
3825         node = static_cast<const SMDS_MeshNode*>( itN->next() );
3826         if ( node->GetPosition()->GetDim() == 2 )
3827           nodeInFace = node;
3828         if ( uvMap.find( node ) == uvMap.end() )
3829           uvCheckNodes.push_back( node );
3830         // add nodes of elems sharing node
3831         //         SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3832         //         while ( eIt->more() ) {
3833         //           const SMDS_MeshElement* e = eIt->next();
3834         //           if ( e != elem ) {
3835         //             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3836         //             while ( nIt->more() ) {
3837         //               const SMDS_MeshNode* n =
3838         //                 static_cast<const SMDS_MeshNode*>( nIt->next() );
3839         //               if ( uvMap.find( n ) == uvMap.end() )
3840         //                 uvCheckNodes.push_back( n );
3841         //             }
3842         //           }
3843         //         }
3844       }
3845       // check UV on face
3846       list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
3847       for ( ; n != uvCheckNodes.end(); ++n ) {
3848         node = *n;
3849         gp_XY uv( 0, 0 );
3850         const SMDS_PositionPtr& pos = node->GetPosition();
3851         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3852         // get existing UV
3853         if ( pos )
3854         {
3855           bool toCheck = true;
3856           uv = helper.GetNodeUV( face, node, nodeInFace, &toCheck );
3857         }
3858         // compute not existing UV
3859         bool project = ( posType == SMDS_TOP_3DSPACE );
3860         // double dist1 = DBL_MAX, dist2 = 0;
3861         // if ( posType != SMDS_TOP_3DSPACE ) {
3862         //   dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
3863         //   project = dist1 > fToler2;
3864         // }
3865         if ( project ) { // compute new UV
3866           gp_XY newUV;
3867           gp_Pnt pNode = SMESH_NodeXYZ( node );
3868           if ( !getClosestUV( projector, pNode, newUV )) {
3869             MESSAGE("Node Projection Failed " << node);
3870           }
3871           else {
3872             if ( isUPeriodic )
3873               newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
3874             if ( isVPeriodic )
3875               newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
3876             // check new UV
3877             // if ( posType != SMDS_TOP_3DSPACE )
3878             //   dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
3879             // if ( dist2 < dist1 )
3880             uv = newUV;
3881           }
3882         }
3883         // store UV in the map
3884         listUV.push_back( uv );
3885         uvMap.insert( make_pair( node, &listUV.back() ));
3886       }
3887     } // loop on not yet smoothed elements
3888
3889     if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
3890       checkBoundaryNodes = true;
3891
3892     // fix nodes on mesh boundary
3893
3894     if ( checkBoundaryNodes ) {
3895       map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
3896       map< SMESH_TLink, int >::iterator link_nb;
3897       // put all elements links to linkNbMap
3898       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3899       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3900         const SMDS_MeshElement* elem = (*elemIt);
3901         int nbn =  elem->NbCornerNodes();
3902         // loop on elem links: insert them in linkNbMap
3903         for ( int iN = 0; iN < nbn; ++iN ) {
3904           const SMDS_MeshNode* n1 = elem->GetNode( iN );
3905           const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
3906           SMESH_TLink link( n1, n2 );
3907           link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
3908           link_nb->second++;
3909         }
3910       }
3911       // remove nodes that are in links encountered only once from setMovableNodes
3912       for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
3913         if ( link_nb->second == 1 ) {
3914           setMovableNodes.erase( link_nb->first.node1() );
3915           setMovableNodes.erase( link_nb->first.node2() );
3916         }
3917       }
3918     }
3919
3920     // -----------------------------------------------------
3921     // for nodes on seam edge, compute one more UV ( uvMap2 );
3922     // find movable nodes linked to nodes on seam and which
3923     // are to be smoothed using the second UV ( uvMap2 )
3924     // -----------------------------------------------------
3925
3926     set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
3927     if ( !surface.IsNull() ) {
3928       TopExp_Explorer eExp( face, TopAbs_EDGE );
3929       for ( ; eExp.More(); eExp.Next() ) {
3930         TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
3931         if ( !BRep_Tool::IsClosed( edge, face ))
3932           continue;
3933         SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
3934         if ( !sm ) continue;
3935         // find out which parameter varies for a node on seam
3936         double f,l;
3937         gp_Pnt2d uv1, uv2;
3938         Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3939         if ( pcurve.IsNull() ) continue;
3940         uv1 = pcurve->Value( f );
3941         edge.Reverse();
3942         pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3943         if ( pcurve.IsNull() ) continue;
3944         uv2 = pcurve->Value( f );
3945         int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
3946         // assure uv1 < uv2
3947         if ( uv1.Coord( iPar ) > uv2.Coord( iPar ))
3948           std::swap( uv1, uv2 );
3949         // get nodes on seam and its vertices
3950         list< const SMDS_MeshNode* > seamNodes;
3951         SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
3952         while ( nSeamIt->more() ) {
3953           const SMDS_MeshNode* node = nSeamIt->next();
3954           if ( !isQuadratic || !IsMedium( node ))
3955             seamNodes.push_back( node );
3956         }
3957         TopExp_Explorer vExp( edge, TopAbs_VERTEX );
3958         for ( ; vExp.More(); vExp.Next() ) {
3959           sm = aMesh->MeshElements( vExp.Current() );
3960           if ( sm ) {
3961             nSeamIt = sm->GetNodes();
3962             while ( nSeamIt->more() )
3963               seamNodes.push_back( nSeamIt->next() );
3964           }
3965         }
3966         // loop on nodes on seam
3967         list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
3968         for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
3969           const SMDS_MeshNode* nSeam = *noSeIt;
3970           map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
3971           if ( n_uv == uvMap.end() )
3972             continue;
3973           // set the first UV
3974           n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
3975           // set the second UV
3976           listUV.push_back( *n_uv->second );
3977           listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
3978           if ( uvMap2.empty() )
3979             uvMap2 = uvMap; // copy the uvMap contents
3980           uvMap2[ nSeam ] = &listUV.back();
3981
3982           // collect movable nodes linked to ones on seam in nodesNearSeam
3983           SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
3984           while ( eIt->more() ) {
3985             const SMDS_MeshElement* e = eIt->next();
3986             int nbUseMap1 = 0, nbUseMap2 = 0;
3987             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3988             int nn = 0, nbn =  e->NbNodes();
3989             if(e->IsQuadratic()) nbn = nbn/2;
3990             while ( nn++ < nbn )
3991             {
3992               const SMDS_MeshNode* n =
3993                 static_cast<const SMDS_MeshNode*>( nIt->next() );
3994               if (n == nSeam ||
3995                   setMovableNodes.find( n ) == setMovableNodes.end() )
3996                 continue;
3997               // add only nodes being closer to uv2 than to uv1
3998               // gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
3999               //              0.5 * ( n->Y() + nSeam->Y() ),
4000               //              0.5 * ( n->Z() + nSeam->Z() ));
4001               // gp_XY uv;
4002               // getClosestUV( projector, pMid, uv );
4003               double x = uvMap[ n ]->Coord( iPar );
4004               if ( Abs( uv1.Coord( iPar ) - x ) >
4005                    Abs( uv2.Coord( iPar ) - x )) {
4006                 nodesNearSeam.insert( n );
4007                 nbUseMap2++;
4008               }
4009               else
4010                 nbUseMap1++;
4011             }
4012             // for centroidalSmooth all element nodes must
4013             // be on one side of a seam
4014             if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
4015               SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4016               nn = 0;
4017               while ( nn++ < nbn ) {
4018                 const SMDS_MeshNode* n =
4019                   static_cast<const SMDS_MeshNode*>( nIt->next() );
4020                 setMovableNodes.erase( n );
4021               }
4022             }
4023           }
4024         } // loop on nodes on seam
4025       } // loop on edge of a face
4026     } // if ( !face.IsNull() )
4027
4028     if ( setMovableNodes.empty() ) {
4029       MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
4030       continue; // goto next face
4031     }
4032
4033     // -------------
4034     // SMOOTHING //
4035     // -------------
4036
4037     int it = -1;
4038     double maxRatio = -1., maxDisplacement = -1.;
4039     set<const SMDS_MeshNode*>::iterator nodeToMove;
4040     for ( it = 0; it < theNbIterations; it++ ) {
4041       maxDisplacement = 0.;
4042       nodeToMove = setMovableNodes.begin();
4043       for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4044         const SMDS_MeshNode* node = (*nodeToMove);
4045         gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
4046
4047         // smooth
4048         bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
4049         if ( theSmoothMethod == LAPLACIAN )
4050           laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
4051         else
4052           centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
4053
4054         // node displacement
4055         gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
4056         Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
4057         if ( aDispl > maxDisplacement )
4058           maxDisplacement = aDispl;
4059       }
4060       // no node movement => exit
4061       //if ( maxDisplacement < 1.e-16 ) {
4062       if ( maxDisplacement < disttol ) {
4063         MESSAGE("-- no node movement --");
4064         break;
4065       }
4066
4067       // check elements quality
4068       maxRatio  = 0;
4069       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4070       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4071         const SMDS_MeshElement* elem = (*elemIt);
4072         if ( !elem || elem->GetType() != SMDSAbs_Face )
4073           continue;
4074         SMESH::Controls::TSequenceOfXYZ aPoints;
4075         if ( aQualityFunc.GetPoints( elem, aPoints )) {
4076           double aValue = aQualityFunc.GetValue( aPoints );
4077           if ( aValue > maxRatio )
4078             maxRatio = aValue;
4079         }
4080       }
4081       if ( maxRatio <= theTgtAspectRatio ) {
4082         //MESSAGE("-- quality achieved --");
4083         break;
4084       }
4085       if (it+1 == theNbIterations) {
4086         //MESSAGE("-- Iteration limit exceeded --");
4087       }
4088     } // smoothing iterations
4089
4090     // MESSAGE(" Face id: " << *fId <<
4091     //         " Nb iterstions: " << it <<
4092     //         " Displacement: " << maxDisplacement <<
4093     //         " Aspect Ratio " << maxRatio);
4094
4095     // ---------------------------------------
4096     // new nodes positions are computed,
4097     // record movement in DS and set new UV
4098     // ---------------------------------------
4099     nodeToMove = setMovableNodes.begin();
4100     for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4101       SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
4102       aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
4103       map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
4104       if ( node_uv != uvMap.end() ) {
4105         gp_XY* uv = node_uv->second;
4106         node->SetPosition
4107           ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
4108       }
4109     }
4110
4111     // move medium nodes of quadratic elements
4112     if ( isQuadratic )
4113     {
4114       vector<const SMDS_MeshNode*> nodes;
4115       bool checkUV;
4116       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4117       for ( ; elemIt != elemsOnFace.end(); ++elemIt )
4118       {
4119         const SMDS_MeshElement* QF = *elemIt;
4120         if ( QF->IsQuadratic() )
4121         {
4122           nodes.assign( SMDS_MeshElement::iterator( QF->interlacedNodesIterator() ),
4123                         SMDS_MeshElement::iterator() );
4124           nodes.push_back( nodes[0] );
4125           gp_Pnt xyz;
4126           for (size_t i = 1; i < nodes.size(); i += 2 ) // i points to a medium node
4127           {
4128             if ( !surface.IsNull() )
4129             {
4130               gp_XY uv1 = helper.GetNodeUV( face, nodes[i-1], nodes[i+1], &checkUV );
4131               gp_XY uv2 = helper.GetNodeUV( face, nodes[i+1], nodes[i-1], &checkUV );
4132               gp_XY uv  = helper.GetMiddleUV( surface, uv1, uv2 );
4133               xyz = surface->Value( uv.X(), uv.Y() );
4134             }
4135             else {
4136               xyz = 0.5 * ( SMESH_NodeXYZ( nodes[i-1] ) + SMESH_NodeXYZ( nodes[i+1] ));
4137             }
4138             if (( SMESH_NodeXYZ( nodes[i] ) - xyz.XYZ() ).Modulus() > disttol )
4139               // we have to move a medium node
4140               aMesh->MoveNode( nodes[i], xyz.X(), xyz.Y(), xyz.Z() );
4141           }
4142         }
4143       }
4144     }
4145
4146   } // loop on face ids
4147
4148 }
4149
4150 namespace
4151 {
4152   //=======================================================================
4153   //function : isReverse
4154   //purpose  : Return true if normal of prevNodes is not co-directied with
4155   //           gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
4156   //           iNotSame is where prevNodes and nextNodes are different.
4157   //           If result is true then future volume orientation is OK
4158   //=======================================================================
4159
4160   bool isReverse(const SMDS_MeshElement*             face,
4161                  const vector<const SMDS_MeshNode*>& prevNodes,
4162                  const vector<const SMDS_MeshNode*>& nextNodes,
4163                  const int                           iNotSame)
4164   {
4165
4166     SMESH_NodeXYZ pP = prevNodes[ iNotSame ];
4167     SMESH_NodeXYZ pN = nextNodes[ iNotSame ];
4168     gp_XYZ extrDir( pN - pP ), faceNorm;
4169     SMESH_MeshAlgos::FaceNormal( face, faceNorm, /*normalized=*/false );
4170
4171     return faceNorm * extrDir < 0.0;
4172   }
4173
4174   //================================================================================
4175   /*!
4176    * \brief Assure that theElemSets[0] holds elements, not nodes
4177    */
4178   //================================================================================
4179
4180   void setElemsFirst( TIDSortedElemSet theElemSets[2] )
4181   {
4182     if ( !theElemSets[0].empty() &&
4183          (*theElemSets[0].begin())->GetType() == SMDSAbs_Node )
4184     {
4185       std::swap( theElemSets[0], theElemSets[1] );
4186     }
4187     else if ( !theElemSets[1].empty() &&
4188               (*theElemSets[1].begin())->GetType() != SMDSAbs_Node )
4189     {
4190       std::swap( theElemSets[0], theElemSets[1] );
4191     }
4192   }
4193 }
4194
4195 //=======================================================================
4196 /*!
4197  * \brief Create elements by sweeping an element
4198  * \param elem - element to sweep
4199  * \param newNodesItVec - nodes generated from each node of the element
4200  * \param newElems - generated elements
4201  * \param nbSteps - number of sweeping steps
4202  * \param srcElements - to append elem for each generated element
4203  */
4204 //=======================================================================
4205
4206 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement*               elem,
4207                                     const vector<TNodeOfNodeListMapItr> & newNodesItVec,
4208                                     list<const SMDS_MeshElement*>&        newElems,
4209                                     const size_t                          nbSteps,
4210                                     SMESH_SequenceOfElemPtr&              srcElements)
4211 {
4212   SMESHDS_Mesh* aMesh = GetMeshDS();
4213
4214   const int           nbNodes = elem->NbNodes();
4215   const int         nbCorners = elem->NbCornerNodes();
4216   SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
4217                                                           polyhedron creation !!! */
4218   // Loop on elem nodes:
4219   // find new nodes and detect same nodes indices
4220   vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
4221   vector<const SMDS_MeshNode*> prevNod( nbNodes );
4222   vector<const SMDS_MeshNode*> nextNod( nbNodes );
4223   vector<const SMDS_MeshNode*> midlNod( nbNodes );
4224
4225   int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
4226   vector<int> sames(nbNodes);
4227   vector<bool> isSingleNode(nbNodes);
4228
4229   for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4230     TNodeOfNodeListMapItr                        nnIt = newNodesItVec[ iNode ];
4231     const SMDS_MeshNode*                         node = nnIt->first;
4232     const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
4233     if ( listNewNodes.empty() )
4234       return;
4235
4236     itNN   [ iNode ] = listNewNodes.begin();
4237     prevNod[ iNode ] = node;
4238     nextNod[ iNode ] = listNewNodes.front();
4239
4240     isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
4241                                                              corner node of linear */
4242     if ( prevNod[ iNode ] != nextNod [ iNode ])
4243       nbDouble += !isSingleNode[iNode];
4244
4245     if( iNode < nbCorners ) { // check corners only
4246       if ( prevNod[ iNode ] == nextNod [ iNode ])
4247         sames[nbSame++] = iNode;
4248       else
4249         iNotSameNode = iNode;
4250     }
4251   }
4252
4253   if ( nbSame == nbNodes || nbSame > 2) {
4254     MESSAGE( " Too many same nodes of element " << elem->GetID() );
4255     return;
4256   }
4257
4258   if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
4259   {
4260     // fix nodes order to have bottom normal external
4261     if ( baseType == SMDSEntity_Polygon )
4262     {
4263       std::reverse( itNN.begin(), itNN.end() );
4264       std::reverse( prevNod.begin(), prevNod.end() );
4265       std::reverse( midlNod.begin(), midlNod.end() );
4266       std::reverse( nextNod.begin(), nextNod.end() );
4267       std::reverse( isSingleNode.begin(), isSingleNode.end() );
4268     }
4269     else
4270     {
4271       const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType, nbNodes );
4272       SMDS_MeshCell::applyInterlace( ind, itNN );
4273       SMDS_MeshCell::applyInterlace( ind, prevNod );
4274       SMDS_MeshCell::applyInterlace( ind, nextNod );
4275       SMDS_MeshCell::applyInterlace( ind, midlNod );
4276       SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4277       if ( nbSame > 0 )
4278       {
4279         sames[nbSame] = iNotSameNode;
4280         for ( int j = 0; j <= nbSame; ++j )
4281           for ( size_t i = 0; i < ind.size(); ++i )
4282             if ( ind[i] == sames[j] )
4283             {
4284               sames[j] = i;
4285               break;
4286             }
4287         iNotSameNode = sames[nbSame];
4288       }
4289     }
4290   }
4291   else if ( elem->GetType() == SMDSAbs_Edge )
4292   {
4293     // orient a new face same as adjacent one
4294     int i1, i2;
4295     const SMDS_MeshElement* e;
4296     TIDSortedElemSet dummy;
4297     if (( e = SMESH_MeshAlgos::FindFaceInSet( nextNod[0], prevNod[0], dummy,dummy, &i1, &i2 )) ||
4298         ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[1], nextNod[1], dummy,dummy, &i1, &i2 )) ||
4299         ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[0], prevNod[1], dummy,dummy, &i1, &i2 )))
4300     {
4301       // there is an adjacent face, check order of nodes in it
4302       bool sameOrder = ( Abs( i2 - i1 ) == 1 ) ? ( i2 > i1 ) : ( i2 < i1 );
4303       if ( sameOrder )
4304       {
4305         std::swap( itNN[0],    itNN[1] );
4306         std::swap( prevNod[0], prevNod[1] );
4307         std::swap( nextNod[0], nextNod[1] );
4308         std::swap( isSingleNode[0], isSingleNode[1] );
4309         if ( nbSame > 0 )
4310           sames[0] = 1 - sames[0];
4311         iNotSameNode = 1 - iNotSameNode;
4312       }
4313     }
4314   }
4315
4316   int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
4317   if ( nbSame > 0 ) {
4318     iSameNode    = sames[ nbSame-1 ];
4319     iBeforeSame  = ( iSameNode + nbCorners - 1 ) % nbCorners;
4320     iAfterSame   = ( iSameNode + 1 ) % nbCorners;
4321     iOpposSame   = ( iSameNode - 2 < 0  ? iSameNode + 2 : iSameNode - 2 );
4322   }
4323
4324   if ( baseType == SMDSEntity_Polygon )
4325   {
4326     if      ( nbNodes == 3 ) baseType = SMDSEntity_Triangle;
4327     else if ( nbNodes == 4 ) baseType = SMDSEntity_Quadrangle;
4328   }
4329   else if ( baseType == SMDSEntity_Quad_Polygon )
4330   {
4331     if      ( nbNodes == 6 ) baseType = SMDSEntity_Quad_Triangle;
4332     else if ( nbNodes == 8 ) baseType = SMDSEntity_Quad_Quadrangle;
4333   }
4334
4335   // make new elements
4336   for ( size_t iStep = 0; iStep < nbSteps; iStep++ )
4337   {
4338     // get next nodes
4339     for ( iNode = 0; iNode < nbNodes; iNode++ )
4340     {
4341       midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
4342       nextNod[ iNode ] = *itNN[ iNode ]++;
4343     }
4344
4345     SMDS_MeshElement* aNewElem = 0;
4346     /*if(!elem->IsPoly())*/ {
4347       switch ( baseType ) {
4348       case SMDSEntity_0D:
4349       case SMDSEntity_Node: { // sweep NODE
4350         if ( nbSame == 0 ) {
4351           if ( isSingleNode[0] )
4352             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
4353           else
4354             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
4355         }
4356         else
4357           return;
4358         break;
4359       }
4360       case SMDSEntity_Edge: { // sweep EDGE
4361         if ( nbDouble == 0 )
4362         {
4363           if ( nbSame == 0 ) // ---> quadrangle
4364             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4365                                       nextNod[ 1 ], nextNod[ 0 ] );
4366           else               // ---> triangle
4367             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4368                                       nextNod[ iNotSameNode ] );
4369         }
4370         else                 // ---> polygon
4371         {
4372           vector<const SMDS_MeshNode*> poly_nodes;
4373           poly_nodes.push_back( prevNod[0] );
4374           poly_nodes.push_back( prevNod[1] );
4375           if ( prevNod[1] != nextNod[1] )
4376           {
4377             if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
4378             poly_nodes.push_back( nextNod[1] );
4379           }
4380           if ( prevNod[0] != nextNod[0] )
4381           {
4382             poly_nodes.push_back( nextNod[0] );
4383             if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
4384           }
4385           switch ( poly_nodes.size() ) {
4386           case 3:
4387             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
4388             break;
4389           case 4:
4390             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
4391                                        poly_nodes[ 2 ], poly_nodes[ 3 ]);
4392             break;
4393           default:
4394             aNewElem = aMesh->AddPolygonalFace (poly_nodes);
4395           }
4396         }
4397         break;
4398       }
4399       case SMDSEntity_Triangle: // TRIANGLE --->
4400       {
4401         if ( nbDouble > 0 ) break;
4402         if ( nbSame == 0 )       // ---> pentahedron
4403           aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4404                                        nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
4405
4406         else if ( nbSame == 1 )  // ---> pyramid
4407           aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4408                                        nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
4409                                        nextNod[ iSameNode ]);
4410
4411         else // 2 same nodes:       ---> tetrahedron
4412           aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4413                                        nextNod[ iNotSameNode ]);
4414         break;
4415       }
4416       case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
4417       {
4418         if ( nbSame == 2 )
4419           return;
4420         if ( nbDouble+nbSame == 2 )
4421         {
4422           if(nbSame==0) {      // ---> quadratic quadrangle
4423             aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4424                                       prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
4425           }
4426           else { //(nbSame==1) // ---> quadratic triangle
4427             if(sames[0]==2) {
4428               return; // medium node on axis
4429             }
4430             else if(sames[0]==0)
4431               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1],
4432                                         prevNod[2], midlNod[1], nextNod[2] );
4433             else // sames[0]==1
4434               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[0],
4435                                         prevNod[2], nextNod[2], midlNod[0]);
4436           }
4437         }
4438         else if ( nbDouble == 3 )
4439         {
4440           if ( nbSame == 0 ) {  // ---> bi-quadratic quadrangle
4441             aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4442                                       prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
4443           }
4444         }
4445         else
4446           return;
4447         break;
4448       }
4449       case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
4450         if ( nbDouble > 0 ) break;
4451
4452         if ( nbSame == 0 )       // ---> hexahedron
4453           aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
4454                                        nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
4455
4456         else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
4457           aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4458                                        nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
4459                                        nextNod[ iSameNode ]);
4460           newElems.push_back( aNewElem );
4461           aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ],  prevNod[ iOpposSame ],
4462                                        prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
4463                                        nextNod[ iOpposSame ],  nextNod[ iBeforeSame ] );
4464         }
4465         else if ( nbSame == 2 ) { // ---> pentahedron
4466           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
4467             // iBeforeSame is same too
4468             aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
4469                                          nextNod[ iOpposSame ],  prevNod[ iSameNode ],
4470                                          prevNod[ iAfterSame ],  nextNod[ iAfterSame ]);
4471           else
4472             // iAfterSame is same too
4473             aNewElem = aMesh->AddVolume (prevNod[ iSameNode ],   prevNod[ iBeforeSame ],
4474                                          nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
4475                                          prevNod[ iOpposSame ],  nextNod[ iOpposSame ]);
4476         }
4477         break;
4478       }
4479       case SMDSEntity_Quad_Triangle:  // sweep (Bi)Quadratic TRIANGLE --->
4480       case SMDSEntity_BiQuad_Triangle: /* ??? */ {
4481         if ( nbDouble+nbSame != 3 ) break;
4482         if(nbSame==0) {
4483           // --->  pentahedron with 15 nodes
4484           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4485                                        nextNod[0], nextNod[1], nextNod[2],
4486                                        prevNod[3], prevNod[4], prevNod[5],
4487                                        nextNod[3], nextNod[4], nextNod[5],
4488                                        midlNod[0], midlNod[1], midlNod[2]);
4489         }
4490         else if(nbSame==1) {
4491           // --->  2d order pyramid of 13 nodes
4492           int apex = iSameNode;
4493           int i0 = ( apex + 1 ) % nbCorners;
4494           int i1 = ( apex - 1 + nbCorners ) % nbCorners;
4495           int i0a = apex + 3;
4496           int i1a = i1 + 3;
4497           int i01 = i0 + 3;
4498           aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
4499                                       nextNod[i0], nextNod[i1], prevNod[apex],
4500                                       prevNod[i01], midlNod[i0],
4501                                       nextNod[i01], midlNod[i1],
4502                                       prevNod[i1a], prevNod[i0a],
4503                                       nextNod[i0a], nextNod[i1a]);
4504         }
4505         else if(nbSame==2) {
4506           // --->  2d order tetrahedron of 10 nodes
4507           int n1 = iNotSameNode;
4508           int n2 = ( n1 + 1             ) % nbCorners;
4509           int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
4510           int n12 = n1 + 3;
4511           int n23 = n2 + 3;
4512           int n31 = n3 + 3;
4513           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
4514                                        prevNod[n12], prevNod[n23], prevNod[n31],
4515                                        midlNod[n1], nextNod[n12], nextNod[n31]);
4516         }
4517         break;
4518       }
4519       case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
4520         if( nbSame == 0 ) {
4521           if ( nbDouble != 4 ) break;
4522           // --->  hexahedron with 20 nodes
4523           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4524                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4525                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4526                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4527                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
4528         }
4529         else if(nbSame==1) {
4530           // ---> pyramid + pentahedron - can not be created since it is needed
4531           // additional middle node at the center of face
4532           //INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
4533           return;
4534         }
4535         else if( nbSame == 2 ) {
4536           if ( nbDouble != 2 ) break;
4537           // --->  2d order Pentahedron with 15 nodes
4538           int n1,n2,n4,n5;
4539           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
4540             // iBeforeSame is same too
4541             n1 = iBeforeSame;
4542             n2 = iOpposSame;
4543             n4 = iSameNode;
4544             n5 = iAfterSame;
4545           }
4546           else {
4547             // iAfterSame is same too
4548             n1 = iSameNode;
4549             n2 = iBeforeSame;
4550             n4 = iAfterSame;
4551             n5 = iOpposSame;
4552           }
4553           int n12 = n2 + 4;
4554           int n45 = n4 + 4;
4555           int n14 = n1 + 4;
4556           int n25 = n5 + 4;
4557           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
4558                                        prevNod[n4], prevNod[n5], nextNod[n5],
4559                                        prevNod[n12], midlNod[n2], nextNod[n12],
4560                                        prevNod[n45], midlNod[n5], nextNod[n45],
4561                                        prevNod[n14], prevNod[n25], nextNod[n25]);
4562         }
4563         break;
4564       }
4565       case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
4566
4567         if( nbSame == 0 && nbDouble == 9 ) {
4568           // --->  tri-quadratic hexahedron with 27 nodes
4569           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4570                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4571                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4572                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4573                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4574                                        prevNod[8], // bottom center
4575                                        midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4576                                        nextNod[8], // top center
4577                                        midlNod[8]);// elem center
4578         }
4579         else
4580         {
4581           return;
4582         }
4583         break;
4584       }
4585       case SMDSEntity_Polygon: { // sweep POLYGON
4586
4587         if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4588           // --->  hexagonal prism
4589           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4590                                        prevNod[3], prevNod[4], prevNod[5],
4591                                        nextNod[0], nextNod[1], nextNod[2],
4592                                        nextNod[3], nextNod[4], nextNod[5]);
4593         }
4594         break;
4595       }
4596       case SMDSEntity_Ball:
4597         return;
4598
4599       default:
4600         break;
4601       } // switch ( baseType )
4602     } // scope
4603
4604     if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4605     {
4606       if ( baseType != SMDSEntity_Polygon )
4607       {
4608         const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType,nbNodes);
4609         SMDS_MeshCell::applyInterlace( ind, prevNod );
4610         SMDS_MeshCell::applyInterlace( ind, nextNod );
4611         SMDS_MeshCell::applyInterlace( ind, midlNod );
4612         SMDS_MeshCell::applyInterlace( ind, itNN );
4613         SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4614         baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4615       }
4616       vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4617       vector<int> quantities (nbNodes + 2);
4618       polyedre_nodes.clear();
4619       quantities.clear();
4620
4621       // bottom of prism
4622       for (int inode = 0; inode < nbNodes; inode++)
4623         polyedre_nodes.push_back( prevNod[inode] );
4624       quantities.push_back( nbNodes );
4625
4626       // top of prism
4627       polyedre_nodes.push_back( nextNod[0] );
4628       for (int inode = nbNodes; inode-1; --inode )
4629         polyedre_nodes.push_back( nextNod[inode-1] );
4630       quantities.push_back( nbNodes );
4631
4632       // side faces
4633       // 3--6--2
4634       // |     |
4635       // 7     5
4636       // |     |
4637       // 0--4--1
4638       const int iQuad = elem->IsQuadratic();
4639       for (int iface = 0; iface < nbNodes; iface += 1+iQuad )
4640       {
4641         const int prevNbNodes = polyedre_nodes.size(); // to detect degenerated face
4642         int inextface = (iface+1+iQuad) % nbNodes;
4643         int imid      = (iface+1) % nbNodes;
4644         polyedre_nodes.push_back( prevNod[inextface] );         // 0
4645         if ( iQuad ) polyedre_nodes.push_back( prevNod[imid] ); // 4
4646         polyedre_nodes.push_back( prevNod[iface] );             // 1
4647         if ( prevNod[iface] != nextNod[iface] ) // 1 != 2
4648         {
4649           if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]); // 5
4650           polyedre_nodes.push_back( nextNod[iface] );                         // 2
4651         }
4652         if ( iQuad ) polyedre_nodes.push_back( nextNod[imid] );               // 6
4653         if ( prevNod[inextface] != nextNod[inextface] ) // 0 != 3
4654         {
4655           polyedre_nodes.push_back( nextNod[inextface] );                            // 3
4656           if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);// 7
4657         }
4658         const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4659         if ( nbFaceNodes > 2 )
4660           quantities.push_back( nbFaceNodes );
4661         else // degenerated face
4662           polyedre_nodes.resize( prevNbNodes );
4663       }
4664       aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4665
4666     } // try to create a polyherdal prism
4667
4668     if ( aNewElem ) {
4669       newElems.push_back( aNewElem );
4670       myLastCreatedElems.push_back(aNewElem);
4671       srcElements.push_back( elem );
4672     }
4673
4674     // set new prev nodes
4675     for ( iNode = 0; iNode < nbNodes; iNode++ )
4676       prevNod[ iNode ] = nextNod[ iNode ];
4677
4678   } // loop on steps
4679 }
4680
4681 //=======================================================================
4682 /*!
4683  * \brief Create 1D and 2D elements around swept elements
4684  * \param mapNewNodes - source nodes and ones generated from them
4685  * \param newElemsMap - source elements and ones generated from them
4686  * \param elemNewNodesMap - nodes generated from each node of each element
4687  * \param elemSet - all swept elements
4688  * \param nbSteps - number of sweeping steps
4689  * \param srcElements - to append elem for each generated element
4690  */
4691 //=======================================================================
4692
4693 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap &     mapNewNodes,
4694                                   TTElemOfElemListMap &    newElemsMap,
4695                                   TElemOfVecOfNnlmiMap &   elemNewNodesMap,
4696                                   TIDSortedElemSet&        elemSet,
4697                                   const int                nbSteps,
4698                                   SMESH_SequenceOfElemPtr& srcElements)
4699 {
4700   ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4701   SMESHDS_Mesh* aMesh = GetMeshDS();
4702
4703   // Find nodes belonging to only one initial element - sweep them into edges.
4704
4705   TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4706   for ( ; nList != mapNewNodes.end(); nList++ )
4707   {
4708     const SMDS_MeshNode* node =
4709       static_cast<const SMDS_MeshNode*>( nList->first );
4710     if ( newElemsMap.count( node ))
4711       continue; // node was extruded into edge
4712     SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4713     int nbInitElems = 0;
4714     const SMDS_MeshElement* el = 0;
4715     SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4716     while ( eIt->more() && nbInitElems < 2 ) {
4717       const SMDS_MeshElement* e = eIt->next();
4718       SMDSAbs_ElementType  type = e->GetType();
4719       if ( type == SMDSAbs_Volume ||
4720            type < highType ||
4721            !elemSet.count(e))
4722         continue;
4723       if ( type > highType ) {
4724         nbInitElems = 0;
4725         highType    = type;
4726       }
4727       el = e;
4728       ++nbInitElems;
4729     }
4730     if ( nbInitElems == 1 ) {
4731       bool NotCreateEdge = el && el->IsMediumNode(node);
4732       if(!NotCreateEdge) {
4733         vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
4734         list<const SMDS_MeshElement*> newEdges;
4735         sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
4736       }
4737     }
4738   }
4739
4740   // Make a ceiling for each element ie an equal element of last new nodes.
4741   // Find free links of faces - make edges and sweep them into faces.
4742
4743   ElemFeatures polyFace( SMDSAbs_Face, /*isPoly=*/true ), anyFace;
4744
4745   TTElemOfElemListMap::iterator  itElem      = newElemsMap.begin();
4746   TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
4747   for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
4748   {
4749     const SMDS_MeshElement* elem = itElem->first;
4750     vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
4751
4752     if(itElem->second.size()==0) continue;
4753
4754     const bool isQuadratic = elem->IsQuadratic();
4755
4756     if ( elem->GetType() == SMDSAbs_Edge ) {
4757       // create a ceiling edge
4758       if ( !isQuadratic ) {
4759         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4760                                vecNewNodes[ 1 ]->second.back())) {
4761           myLastCreatedElems.push_back(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4762                                                       vecNewNodes[ 1 ]->second.back()));
4763           srcElements.push_back( elem );
4764         }
4765       }
4766       else {
4767         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4768                                vecNewNodes[ 1 ]->second.back(),
4769                                vecNewNodes[ 2 ]->second.back())) {
4770           myLastCreatedElems.push_back(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4771                                                       vecNewNodes[ 1 ]->second.back(),
4772                                                       vecNewNodes[ 2 ]->second.back()));
4773           srcElements.push_back( elem );
4774         }
4775       }
4776     }
4777     if ( elem->GetType() != SMDSAbs_Face )
4778       continue;
4779
4780     bool hasFreeLinks = false;
4781
4782     TIDSortedElemSet avoidSet;
4783     avoidSet.insert( elem );
4784
4785     set<const SMDS_MeshNode*> aFaceLastNodes;
4786     int iNode, nbNodes = vecNewNodes.size();
4787     if ( !isQuadratic ) {
4788       // loop on the face nodes
4789       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4790         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4791         // look for free links of the face
4792         int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
4793         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4794         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4795         // check if a link n1-n2 is free
4796         if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
4797           hasFreeLinks = true;
4798           // make a new edge and a ceiling for a new edge
4799           const SMDS_MeshElement* edge;
4800           if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
4801             myLastCreatedElems.push_back( edge = aMesh->AddEdge( n1, n2 )); // free link edge
4802             srcElements.push_back( myLastCreatedElems.back() );
4803           }
4804           n1 = vecNewNodes[ iNode ]->second.back();
4805           n2 = vecNewNodes[ iNext ]->second.back();
4806           if ( !aMesh->FindEdge( n1, n2 )) {
4807             myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2 )); // new edge ceiling
4808             srcElements.push_back( edge );
4809           }
4810         }
4811       }
4812     }
4813     else { // elem is quadratic face
4814       int nbn = nbNodes/2;
4815       for ( iNode = 0; iNode < nbn; iNode++ ) {
4816         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4817         int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
4818         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4819         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4820         const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
4821         // check if a link is free
4822         if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
4823              ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
4824              ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
4825           hasFreeLinks = true;
4826           // make an edge and a ceiling for a new edge
4827           // find medium node
4828           if ( !aMesh->FindEdge( n1, n2, n3 )) {
4829             myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2, n3 )); // free link edge
4830             srcElements.push_back( elem );
4831           }
4832           n1 = vecNewNodes[ iNode ]->second.back();
4833           n2 = vecNewNodes[ iNext ]->second.back();
4834           n3 = vecNewNodes[ iNode+nbn ]->second.back();
4835           if ( !aMesh->FindEdge( n1, n2, n3 )) {
4836             myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
4837             srcElements.push_back( elem );
4838           }
4839         }
4840       }
4841       for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
4842         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4843       }
4844     }
4845
4846     // sweep free links into faces
4847
4848     if ( hasFreeLinks ) {
4849       list<const SMDS_MeshElement*> & newVolumes = itElem->second;
4850       int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
4851
4852       set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
4853       set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
4854       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4855         initNodeSet.insert( vecNewNodes[ iNode ]->first );
4856         topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
4857       }
4858       if ( isQuadratic && nbNodes % 2 ) {  // node set for the case of a biquadratic
4859         initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
4860         initNodeSetNoCenter.erase( vecNewNodes.back()->first );
4861       }
4862       for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
4863         list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
4864         std::advance( v, volNb );
4865         // find indices of free faces of a volume and their source edges
4866         list< int > freeInd;
4867         list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
4868         SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
4869         int iF, nbF = vTool.NbFaces();
4870         for ( iF = 0; iF < nbF; iF ++ ) {
4871           if ( vTool.IsFreeFace( iF ) &&
4872                vTool.GetFaceNodes( iF, faceNodeSet ) &&
4873                initNodeSet != faceNodeSet) // except an initial face
4874           {
4875             if ( nbSteps == 1 && faceNodeSet == topNodeSet )
4876               continue;
4877             if ( faceNodeSet == initNodeSetNoCenter )
4878               continue;
4879             freeInd.push_back( iF );
4880             // find source edge of a free face iF
4881             vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
4882             vector<const SMDS_MeshNode*>::iterator lastCommom;
4883             commonNodes.resize( nbNodes, 0 );
4884             lastCommom = std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
4885                                                 initNodeSet.begin(), initNodeSet.end(),
4886                                                 commonNodes.begin());
4887             if ( std::distance( commonNodes.begin(), lastCommom ) == 3 )
4888               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
4889             else
4890               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
4891 #ifdef _DEBUG_
4892             if ( !srcEdges.back() )
4893             {
4894               cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
4895                    << iF << " of volume #" << vTool.ID() << endl;
4896             }
4897 #endif
4898           }
4899         }
4900         if ( freeInd.empty() )
4901           continue;
4902
4903         // create wall faces for all steps;
4904         // if such a face has been already created by sweep of edge,
4905         // assure that its orientation is OK
4906         for ( int iStep = 0; iStep < nbSteps; iStep++ )
4907         {
4908           vTool.Set( *v, /*ignoreCentralNodes=*/false );
4909           vTool.SetExternalNormal();
4910           const int nextShift = vTool.IsForward() ? +1 : -1;
4911           list< int >::iterator ind = freeInd.begin();
4912           list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
4913           for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
4914           {
4915             const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
4916             int nbn = vTool.NbFaceNodes( *ind );
4917             const SMDS_MeshElement * f = 0;
4918             if ( nbn == 3 )              ///// triangle
4919             {
4920               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
4921               if ( !f ||
4922                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4923               {
4924                 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
4925                                                      nodes[ 1 ],
4926                                                      nodes[ 1 + nextShift ] };
4927                 if ( f )
4928                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4929                 else
4930                   myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4931                                                                newOrder[ 2 ] ));
4932               }
4933             }
4934             else if ( nbn == 4 )       ///// quadrangle
4935             {
4936               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
4937               if ( !f ||
4938                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4939               {
4940                 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
4941                                                      nodes[ 2 ], nodes[ 2+nextShift ] };
4942                 if ( f )
4943                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4944                 else
4945                   myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4946                                                                newOrder[ 2 ], newOrder[ 3 ]));
4947               }
4948             }
4949             else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
4950             {
4951               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
4952               if ( !f ||
4953                    nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
4954               {
4955                 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
4956                                                      nodes[2],
4957                                                      nodes[2 + 2*nextShift],
4958                                                      nodes[3 - 2*nextShift],
4959                                                      nodes[3],
4960                                                      nodes[3 + 2*nextShift]};
4961                 if ( f )
4962                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4963                 else
4964                   myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ],
4965                                                                newOrder[ 1 ],
4966                                                                newOrder[ 2 ],
4967                                                                newOrder[ 3 ],
4968                                                                newOrder[ 4 ],
4969                                                                newOrder[ 5 ] ));
4970               }
4971             }
4972             else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
4973             {
4974               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
4975                                    nodes[1], nodes[3], nodes[5], nodes[7] );
4976               if ( !f ||
4977                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4978               {
4979                 const SMDS_MeshNode* newOrder[8] = { nodes[0],
4980                                                      nodes[4 - 2*nextShift],
4981                                                      nodes[4],
4982                                                      nodes[4 + 2*nextShift],
4983                                                      nodes[1],
4984                                                      nodes[5 - 2*nextShift],
4985                                                      nodes[5],
4986                                                      nodes[5 + 2*nextShift] };
4987                 if ( f )
4988                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4989                 else
4990                   myLastCreatedElems.push_back(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4991                                                               newOrder[ 2 ], newOrder[ 3 ],
4992                                                               newOrder[ 4 ], newOrder[ 5 ],
4993                                                               newOrder[ 6 ], newOrder[ 7 ]));
4994               }
4995             }
4996             else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
4997             {
4998               f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
4999                                       SMDSAbs_Face, /*noMedium=*/false);
5000               if ( !f ||
5001                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5002               {
5003                 const SMDS_MeshNode* newOrder[9] = { nodes[0],
5004                                                      nodes[4 - 2*nextShift],
5005                                                      nodes[4],
5006                                                      nodes[4 + 2*nextShift],
5007                                                      nodes[1],
5008                                                      nodes[5 - 2*nextShift],
5009                                                      nodes[5],
5010                                                      nodes[5 + 2*nextShift],
5011                                                      nodes[8] };
5012                 if ( f )
5013                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5014                 else
5015                   myLastCreatedElems.push_back(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5016                                                               newOrder[ 2 ], newOrder[ 3 ],
5017                                                               newOrder[ 4 ], newOrder[ 5 ],
5018                                                               newOrder[ 6 ], newOrder[ 7 ],
5019                                                               newOrder[ 8 ]));
5020               }
5021             }
5022             else  //////// polygon
5023             {
5024               vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
5025               const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
5026               if ( !f ||
5027                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
5028               {
5029                 if ( !vTool.IsForward() )
5030                   std::reverse( polygon_nodes.begin(), polygon_nodes.end());
5031                 if ( f )
5032                   aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
5033                 else
5034                   AddElement( polygon_nodes, polyFace.SetQuad( (*v)->IsQuadratic() ));
5035               }
5036             }
5037
5038             while ( srcElements.size() < myLastCreatedElems.size() )
5039               srcElements.push_back( *srcEdge );
5040
5041           }  // loop on free faces
5042
5043           // go to the next volume
5044           iVol = 0;
5045           while ( iVol++ < nbVolumesByStep ) v++;
5046
5047         } // loop on steps
5048       } // loop on volumes of one step
5049     } // sweep free links into faces
5050
5051     // Make a ceiling face with a normal external to a volume
5052
5053     // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
5054     SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
5055     int iF = lastVol.GetFaceIndex( aFaceLastNodes );
5056
5057     if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
5058       aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
5059       iF = lastVol.GetFaceIndex( aFaceLastNodes );
5060     }
5061     if ( iF >= 0 )
5062     {
5063       lastVol.SetExternalNormal();
5064       const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
5065       const               int nbn = lastVol.NbFaceNodes( iF );
5066       vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
5067       if ( !hasFreeLinks ||
5068            !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
5069       {
5070         const vector<int>& interlace =
5071           SMDS_MeshCell::interlacedSmdsOrder( elem->GetEntityType(), nbn );
5072         SMDS_MeshCell::applyInterlaceRev( interlace, nodeVec );
5073
5074         AddElement( nodeVec, anyFace.Init( elem ));
5075
5076         while ( srcElements.size() < myLastCreatedElems.size() )
5077           srcElements.push_back( elem );
5078       }
5079     }
5080   } // loop on swept elements
5081 }
5082
5083 //=======================================================================
5084 //function : RotationSweep
5085 //purpose  :
5086 //=======================================================================
5087
5088 SMESH_MeshEditor::PGroupIDs
5089 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet   theElemSets[2],
5090                                 const gp_Ax1&      theAxis,
5091                                 const double       theAngle,
5092                                 const int          theNbSteps,
5093                                 const double       theTol,
5094                                 const bool         theMakeGroups,
5095                                 const bool         theMakeWalls)
5096 {
5097   ClearLastCreated();
5098
5099   setElemsFirst( theElemSets );
5100   myLastCreatedElems.reserve( theElemSets[0].size() * theNbSteps );
5101   myLastCreatedNodes.reserve( theElemSets[1].size() * theNbSteps );
5102
5103   // source elements for each generated one
5104   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5105   srcElems.reserve( theElemSets[0].size() );
5106   srcNodes.reserve( theElemSets[1].size() );
5107
5108   gp_Trsf aTrsf;
5109   aTrsf.SetRotation( theAxis, theAngle );
5110   gp_Trsf aTrsf2;
5111   aTrsf2.SetRotation( theAxis, theAngle/2. );
5112
5113   gp_Lin aLine( theAxis );
5114   double aSqTol = theTol * theTol;
5115
5116   SMESHDS_Mesh* aMesh = GetMeshDS();
5117
5118   TNodeOfNodeListMap mapNewNodes;
5119   TElemOfVecOfNnlmiMap mapElemNewNodes;
5120   TTElemOfElemListMap newElemsMap;
5121
5122   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5123                                      myMesh->NbFaces(ORDER_QUADRATIC) +
5124                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
5125   // loop on theElemSets
5126   TIDSortedElemSet::iterator itElem;
5127   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5128   {
5129     TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5130     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5131       const SMDS_MeshElement* elem = *itElem;
5132       if ( !elem || elem->GetType() == SMDSAbs_Volume )
5133         continue;
5134       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5135       newNodesItVec.reserve( elem->NbNodes() );
5136
5137       // loop on elem nodes
5138       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5139       while ( itN->more() )
5140       {
5141         const SMDS_MeshNode* node = cast2Node( itN->next() );
5142
5143         gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
5144         double coord[3];
5145         aXYZ.Coord( coord[0], coord[1], coord[2] );
5146         bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
5147
5148         // check if a node has been already sweeped
5149         TNodeOfNodeListMapItr nIt =
5150           mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5151         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5152         if ( listNewNodes.empty() )
5153         {
5154           // check if we are to create medium nodes between corner ones
5155           bool needMediumNodes = false;
5156           if ( isQuadraticMesh )
5157           {
5158             SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5159             while (it->more() && !needMediumNodes )
5160             {
5161               const SMDS_MeshElement* invElem = it->next();
5162               if ( invElem != elem && !theElems.count( invElem )) continue;
5163               needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5164               if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5165                 needMediumNodes = true;
5166             }
5167           }
5168
5169           // make new nodes
5170           const SMDS_MeshNode * newNode = node;
5171           for ( int i = 0; i < theNbSteps; i++ ) {
5172             if ( !isOnAxis ) {
5173               if ( needMediumNodes )  // create a medium node
5174               {
5175                 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5176                 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5177                 myLastCreatedNodes.push_back(newNode);
5178                 srcNodes.push_back( node );
5179                 listNewNodes.push_back( newNode );
5180                 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5181               }
5182               else {
5183                 aTrsf.Transforms( coord[0], coord[1], coord[2] );
5184               }
5185               // create a corner node
5186               newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5187               myLastCreatedNodes.push_back(newNode);
5188               srcNodes.push_back( node );
5189               listNewNodes.push_back( newNode );
5190             }
5191             else {
5192               listNewNodes.push_back( newNode );
5193               // if ( needMediumNodes )
5194               //   listNewNodes.push_back( newNode );
5195             }
5196           }
5197         }
5198         newNodesItVec.push_back( nIt );
5199       }
5200       // make new elements
5201       sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
5202     }
5203   }
5204
5205   if ( theMakeWalls )
5206     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], theNbSteps, srcElems );
5207
5208   PGroupIDs newGroupIDs;
5209   if ( theMakeGroups )
5210     newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
5211
5212   return newGroupIDs;
5213 }
5214
5215 //=======================================================================
5216 //function : ExtrusParam
5217 //purpose  : standard construction
5218 //=======================================================================
5219
5220 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Vec&            theStep,
5221                                             const int                theNbSteps,
5222                                             const std::list<double>& theScales,
5223                                             const std::list<double>& theAngles,
5224                                             const gp_XYZ*            theBasePoint,
5225                                             const int                theFlags,
5226                                             const double             theTolerance):
5227   myDir( theStep ),
5228   myBaseP( Precision::Infinite(), 0, 0 ),
5229   myFlags( theFlags ),
5230   myTolerance( theTolerance ),
5231   myElemsToUse( NULL )
5232 {
5233   mySteps = new TColStd_HSequenceOfReal;
5234   const double stepSize = theStep.Magnitude();
5235   for (int i=1; i<=theNbSteps; i++ )
5236     mySteps->Append( stepSize );
5237
5238   if ( !theScales.empty() )
5239   {
5240     if ( IsScaleVariation() && (int)theScales.size() < theNbSteps )
5241       linearScaleVariation( theNbSteps, const_cast< std::list<double>& >( theScales ));
5242
5243     // add medium scales
5244     std::list<double>::const_iterator s2 = theScales.begin(), s1 = s2++;
5245     myScales.reserve( theNbSteps * 2 );
5246     myScales.push_back( 0.5 * ( *s1 + 1. ));
5247     myScales.push_back( *s1 );
5248     for ( ; s2 != theScales.end(); s1 = s2++ )
5249     {
5250       myScales.push_back( 0.5 * ( *s1 + *s2 ));
5251       myScales.push_back( *s2 );
5252     }
5253   }
5254
5255   if ( !theAngles.empty() )
5256   {
5257     std::list<double>& angles = const_cast< std::list<double>& >( theAngles );
5258     if ( IsAngleVariation() && (int)theAngles.size() < theNbSteps )
5259       linearAngleVariation( theNbSteps, angles );
5260
5261     // accumulate angles
5262     double angle = 0;
5263     int nbAngles = 0;
5264     std::list<double>::iterator a1 = angles.begin(), a2;
5265     for ( ; a1 != angles.end(); ++a1, ++nbAngles )
5266     {
5267       angle += *a1;
5268       *a1 = angle;
5269     }
5270     while ( nbAngles++ < theNbSteps )
5271       angles.push_back( angles.back() );
5272
5273     // add medium angles
5274     a2 = angles.begin(), a1 = a2++;
5275     myAngles.push_back( 0.5 * *a1 );
5276     myAngles.push_back( *a1 );
5277     for ( ; a2 != angles.end(); a1 = a2++ )
5278     {
5279       myAngles.push_back( 0.5 * ( *a1 + *a2 ));
5280       myAngles.push_back( *a2 );
5281     }
5282   }
5283
5284   if ( theBasePoint )
5285   {
5286     myBaseP = *theBasePoint;
5287   }
5288
5289   if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5290       ( theTolerance > 0 ))
5291   {
5292     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5293   }
5294   else
5295   {
5296     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5297   }
5298 }
5299
5300 //=======================================================================
5301 //function : ExtrusParam
5302 //purpose  : steps are given explicitly
5303 //=======================================================================
5304
5305 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Dir&                   theDir,
5306                                             Handle(TColStd_HSequenceOfReal) theSteps,
5307                                             const int                       theFlags,
5308                                             const double                    theTolerance):
5309   myDir( theDir ),
5310   mySteps( theSteps ),
5311   myFlags( theFlags ),
5312   myTolerance( theTolerance ),
5313   myElemsToUse( NULL )
5314 {
5315   if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5316       ( theTolerance > 0 ))
5317   {
5318     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5319   }
5320   else
5321   {
5322     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5323   }
5324 }
5325
5326 //=======================================================================
5327 //function : ExtrusParam
5328 //purpose  : for extrusion by normal
5329 //=======================================================================
5330
5331 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const double theStepSize,
5332                                             const int    theNbSteps,
5333                                             const int    theFlags,
5334                                             const int    theDim ):
5335   myDir( 1,0,0 ),
5336   mySteps( new TColStd_HSequenceOfReal ),
5337   myFlags( theFlags ),
5338   myTolerance( 0 ),
5339   myElemsToUse( NULL )
5340 {
5341   for (int i = 0; i < theNbSteps; i++ )
5342     mySteps->Append( theStepSize );
5343
5344   if ( theDim == 1 )
5345   {
5346     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal1D;
5347   }
5348   else
5349   {
5350     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal2D;
5351   }
5352 }
5353
5354 //=======================================================================
5355 //function : ExtrusParam
5356 //purpose  : for extrusion along path
5357 //=======================================================================
5358
5359 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const std::vector< PathPoint >& thePoints,
5360                                             const gp_Pnt*                   theBasePoint,
5361                                             const std::list<double>&        theScales,
5362                                             const bool                      theMakeGroups )
5363   : myBaseP( Precision::Infinite(), 0, 0 ),
5364     myFlags( EXTRUSION_FLAG_BOUNDARY | ( theMakeGroups ? EXTRUSION_FLAG_GROUPS : 0 )),
5365     myPathPoints( thePoints )
5366 {
5367   if ( theBasePoint )
5368   {
5369     myBaseP = theBasePoint->XYZ();
5370   }
5371
5372   if ( !theScales.empty() )
5373   {
5374     // add medium scales
5375     std::list<double>::const_iterator s2 = theScales.begin(), s1 = s2++;
5376     myScales.reserve( thePoints.size() * 2 );
5377     myScales.push_back( 0.5 * ( 1. + *s1 ));
5378     myScales.push_back( *s1 );
5379     for ( ; s2 != theScales.end(); s1 = s2++ )
5380     {
5381       myScales.push_back( 0.5 * ( *s1 + *s2 ));
5382       myScales.push_back( *s2 );
5383     }
5384   }
5385
5386   myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesAlongTrack;
5387 }
5388
5389 //=======================================================================
5390 //function : ExtrusParam::SetElementsToUse
5391 //purpose  : stores elements to use for extrusion by normal, depending on
5392 //           state of EXTRUSION_FLAG_USE_INPUT_ELEMS_ONLY flag;
5393 //           define myBaseP for scaling
5394 //=======================================================================
5395
5396 void SMESH_MeshEditor::ExtrusParam::SetElementsToUse( const TIDSortedElemSet& elems,
5397                                                       const TIDSortedElemSet& nodes )
5398 {
5399   myElemsToUse = ToUseInpElemsOnly() ? & elems : 0;
5400
5401   if ( Precision::IsInfinite( myBaseP.X() )) // myBaseP not defined
5402   {
5403     myBaseP.SetCoord( 0.,0.,0. );
5404     TIDSortedElemSet newNodes;
5405
5406     const TIDSortedElemSet* elemSets[] = { &elems, &nodes };
5407     for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5408     {
5409       const TIDSortedElemSet& elements = *( elemSets[ is2ndSet ]);
5410       TIDSortedElemSet::const_iterator itElem = elements.begin();
5411       for ( ; itElem != elements.end(); itElem++ )
5412       {
5413         const SMDS_MeshElement* elem = *itElem;
5414         SMDS_ElemIteratorPtr     itN = elem->nodesIterator();
5415         while ( itN->more() ) {
5416           const SMDS_MeshElement* node = itN->next();
5417           if ( newNodes.insert( node ).second )
5418             myBaseP += SMESH_NodeXYZ( node );
5419         }
5420       }
5421     }
5422     myBaseP /= newNodes.size();
5423   }
5424 }
5425
5426 //=======================================================================
5427 //function : ExtrusParam::beginStepIter
5428 //purpose  : prepare iteration on steps
5429 //=======================================================================
5430
5431 void SMESH_MeshEditor::ExtrusParam::beginStepIter( bool withMediumNodes )
5432 {
5433   myWithMediumNodes = withMediumNodes;
5434   myNextStep = 1;
5435   myCurSteps.clear();
5436 }
5437 //=======================================================================
5438 //function : ExtrusParam::moreSteps
5439 //purpose  : are there more steps?
5440 //=======================================================================
5441
5442 bool SMESH_MeshEditor::ExtrusParam::moreSteps()
5443 {
5444   return myNextStep <= mySteps->Length() || !myCurSteps.empty();
5445 }
5446 //=======================================================================
5447 //function : ExtrusParam::nextStep
5448 //purpose  : returns the next step
5449 //=======================================================================
5450
5451 double SMESH_MeshEditor::ExtrusParam::nextStep()
5452 {
5453   double res = 0;
5454   if ( !myCurSteps.empty() )
5455   {
5456     res = myCurSteps.back();
5457     myCurSteps.pop_back();
5458   }
5459   else if ( myNextStep <= mySteps->Length() )
5460   {
5461     myCurSteps.push_back( mySteps->Value( myNextStep ));
5462     ++myNextStep;
5463     if ( myWithMediumNodes )
5464     {
5465       myCurSteps.back() /= 2.;
5466       myCurSteps.push_back( myCurSteps.back() );
5467     }
5468     res = nextStep();
5469   }
5470   return res;
5471 }
5472
5473 //=======================================================================
5474 //function : ExtrusParam::makeNodesByDir
5475 //purpose  : create nodes for standard extrusion
5476 //=======================================================================
5477
5478 int SMESH_MeshEditor::ExtrusParam::
5479 makeNodesByDir( SMESHDS_Mesh*                     mesh,
5480                 const SMDS_MeshNode*              srcNode,
5481                 std::list<const SMDS_MeshNode*> & newNodes,
5482                 const bool                        makeMediumNodes)
5483 {
5484   gp_XYZ p = SMESH_NodeXYZ( srcNode );
5485
5486   int nbNodes = 0;
5487   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5488   {
5489     p += myDir.XYZ() * nextStep();
5490     const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5491     newNodes.push_back( newNode );
5492   }
5493
5494   if ( !myScales.empty() || !myAngles.empty() )
5495   {
5496     gp_XYZ  center = myBaseP;
5497     gp_Ax1  ratationAxis( center, myDir );
5498     gp_Trsf rotation;
5499
5500     std::list<const SMDS_MeshNode*>::iterator nIt = newNodes.begin();
5501     size_t i = !makeMediumNodes;
5502     for ( beginStepIter( makeMediumNodes );
5503           moreSteps();
5504           ++nIt, i += 1 + !makeMediumNodes )
5505     {
5506       center += myDir.XYZ() * nextStep();
5507
5508       gp_XYZ xyz = SMESH_NodeXYZ( *nIt );
5509       bool moved = false;
5510       if ( i < myScales.size() )
5511       {
5512         xyz = ( myScales[i] * ( xyz - center )) + center;
5513         moved = true;
5514       }
5515       if ( !myAngles.empty() )
5516       {
5517         rotation.SetRotation( ratationAxis, myAngles[i] );
5518         rotation.Transforms( xyz );
5519         moved = true;
5520       }
5521       if ( moved )
5522         mesh->MoveNode( *nIt, xyz.X(), xyz.Y(), xyz.Z() );
5523       else
5524         break;
5525     }
5526   }
5527   return nbNodes;
5528 }
5529
5530 //=======================================================================
5531 //function : ExtrusParam::makeNodesByDirAndSew
5532 //purpose  : create nodes for standard extrusion with sewing
5533 //=======================================================================
5534
5535 int SMESH_MeshEditor::ExtrusParam::
5536 makeNodesByDirAndSew( SMESHDS_Mesh*                     mesh,
5537                       const SMDS_MeshNode*              srcNode,
5538                       std::list<const SMDS_MeshNode*> & newNodes,
5539                       const bool                        makeMediumNodes)
5540 {
5541   gp_XYZ P1 = SMESH_NodeXYZ( srcNode );
5542
5543   int nbNodes = 0;
5544   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5545   {
5546     P1 += myDir.XYZ() * nextStep();
5547
5548     // try to search in sequence of existing nodes
5549     // if myNodes.size()>0 we 'nave to use given sequence
5550     // else - use all nodes of mesh
5551     const SMDS_MeshNode * node = 0;
5552     if ( myNodes.Length() > 0 )
5553     {
5554       for ( int i = 1; i <= myNodes.Length(); i++ )
5555       {
5556         SMESH_NodeXYZ P2 = myNodes.Value(i);
5557         if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5558         {
5559           node = myNodes.Value(i);
5560           break;
5561         }
5562       }
5563     }
5564     else
5565     {
5566       SMDS_NodeIteratorPtr itn = mesh->nodesIterator();
5567       while(itn->more())
5568       {
5569         SMESH_NodeXYZ P2 = itn->next();
5570         if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5571         {
5572           node = P2._node;
5573           break;
5574         }
5575       }
5576     }
5577
5578     if ( !node )
5579       node = mesh->AddNode( P1.X(), P1.Y(), P1.Z() );
5580
5581     newNodes.push_back( node );
5582
5583   } // loop on steps
5584
5585   return nbNodes;
5586 }
5587
5588 //=======================================================================
5589 //function : ExtrusParam::makeNodesByNormal2D
5590 //purpose  : create nodes for extrusion using normals of faces
5591 //=======================================================================
5592
5593 int SMESH_MeshEditor::ExtrusParam::
5594 makeNodesByNormal2D( SMESHDS_Mesh*                     mesh,
5595                      const SMDS_MeshNode*              srcNode,
5596                      std::list<const SMDS_MeshNode*> & newNodes,
5597                      const bool                        makeMediumNodes)
5598 {
5599   const bool alongAvgNorm = ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL );
5600
5601   gp_XYZ p = SMESH_NodeXYZ( srcNode );
5602
5603   // get normals to faces sharing srcNode
5604   vector< gp_XYZ > norms, baryCenters;
5605   gp_XYZ norm, avgNorm( 0,0,0 );
5606   SMDS_ElemIteratorPtr faceIt = srcNode->GetInverseElementIterator( SMDSAbs_Face );
5607   while ( faceIt->more() )
5608   {
5609     const SMDS_MeshElement* face = faceIt->next();
5610     if ( myElemsToUse && !myElemsToUse->count( face ))
5611       continue;
5612     if ( SMESH_MeshAlgos::FaceNormal( face, norm, /*normalized=*/true ))
5613     {
5614       norms.push_back( norm );
5615       avgNorm += norm;
5616       if ( !alongAvgNorm )
5617       {
5618         gp_XYZ bc(0,0,0);
5619         int nbN = 0;
5620         for ( SMDS_ElemIteratorPtr nIt = face->nodesIterator(); nIt->more(); ++nbN )
5621           bc += SMESH_NodeXYZ( nIt->next() );
5622         baryCenters.push_back( bc / nbN );
5623       }
5624     }
5625   }
5626
5627   if ( norms.empty() ) return 0;
5628
5629   double normSize = avgNorm.Modulus();
5630   if ( normSize < std::numeric_limits<double>::min() )
5631     return 0;
5632
5633   if ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL ) // extrude along avgNorm
5634   {
5635     myDir = avgNorm;
5636     return makeNodesByDir( mesh, srcNode, newNodes, makeMediumNodes );
5637   }
5638
5639   avgNorm /= normSize;
5640
5641   int nbNodes = 0;
5642   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5643   {
5644     gp_XYZ pNew = p;
5645     double stepSize = nextStep();
5646
5647     if ( norms.size() > 1 )
5648     {
5649       for ( size_t iF = 0; iF < norms.size(); ++iF ) // loop on faces
5650       {
5651         // translate plane of a face
5652         baryCenters[ iF ] += norms[ iF ] * stepSize;
5653
5654         // find point of intersection of the face plane located at baryCenters[ iF ]
5655         // and avgNorm located at pNew
5656         double d    = -( norms[ iF ] * baryCenters[ iF ]); // d of plane equation ax+by+cz+d=0
5657         double dot  = ( norms[ iF ] * avgNorm );
5658         if ( dot < std::numeric_limits<double>::min() )
5659           dot = stepSize * 1e-3;
5660         double step = -( norms[ iF ] * pNew + d ) / dot;
5661         pNew += step * avgNorm;
5662       }
5663     }
5664     else
5665     {
5666       pNew += stepSize * avgNorm;
5667     }
5668     p = pNew;
5669
5670     const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5671     newNodes.push_back( newNode );
5672   }
5673   return nbNodes;
5674 }
5675
5676 //=======================================================================
5677 //function : ExtrusParam::makeNodesByNormal1D
5678 //purpose  : create nodes for extrusion using normals of edges
5679 //=======================================================================
5680
5681 int SMESH_MeshEditor::ExtrusParam::
5682 makeNodesByNormal1D( SMESHDS_Mesh*                     /*mesh*/,
5683                      const SMDS_MeshNode*              /*srcNode*/,
5684                      std::list<const SMDS_MeshNode*> & /*newNodes*/,
5685                      const bool                        /*makeMediumNodes*/)
5686 {
5687   throw SALOME_Exception("Extrusion 1D by Normal not implemented");
5688   return 0;
5689 }
5690
5691 //=======================================================================
5692 //function : ExtrusParam::makeNodesAlongTrack
5693 //purpose  : create nodes for extrusion along path
5694 //=======================================================================
5695
5696 int SMESH_MeshEditor::ExtrusParam::
5697 makeNodesAlongTrack( SMESHDS_Mesh*                     mesh,
5698                      const SMDS_MeshNode*              srcNode,
5699                      std::list<const SMDS_MeshNode*> & newNodes,
5700                      const bool                        makeMediumNodes)
5701 {
5702   const Standard_Real aTolAng=1.e-4;
5703
5704   gp_Pnt aV0x = myBaseP;
5705   gp_Pnt aPN0 = SMESH_NodeXYZ( srcNode );
5706
5707   const PathPoint& aPP0 = myPathPoints[0];
5708   gp_Pnt aP0x = aPP0.myPnt;
5709   gp_Dir aDT0x= aPP0.myTgt;
5710
5711   std::vector< gp_Pnt > centers;
5712   centers.reserve( NbSteps() * 2 );
5713
5714   gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
5715
5716   for ( size_t j = 1; j < myPathPoints.size(); ++j )
5717   {
5718     const PathPoint&  aPP  = myPathPoints[j];
5719     const gp_Pnt&     aP1x = aPP.myPnt;
5720     const gp_Dir&    aDT1x = aPP.myTgt;
5721
5722     // Translation
5723     gp_Vec aV01x( aP0x, aP1x );
5724     aTrsf.SetTranslation( aV01x );
5725     gp_Pnt aV1x = aV0x.Transformed( aTrsf );
5726     gp_Pnt aPN1 = aPN0.Transformed( aTrsf );
5727
5728     // rotation 1 [ T1,T0 ]
5729     Standard_Real aAngleT1T0 = -aDT1x.Angle( aDT0x );
5730     if ( fabs( aAngleT1T0 ) > aTolAng )
5731     {
5732       gp_Dir aDT1T0 = aDT1x ^ aDT0x;
5733       aTrsfRotT1T0.SetRotation( gp_Ax1( aV1x, aDT1T0 ), aAngleT1T0 );
5734
5735       aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
5736     }
5737
5738     // rotation 2
5739     if ( aPP.myAngle != 0. )
5740     {
5741       aTrsfRot.SetRotation( gp_Ax1( aV1x, aDT1x ), aPP.myAngle );
5742       aPN1 = aPN1.Transformed( aTrsfRot );
5743     }
5744
5745     // make new node
5746     if ( makeMediumNodes )
5747     {
5748       // create additional node
5749       gp_XYZ midP = 0.5 * ( aPN1.XYZ() + aPN0.XYZ() );
5750       const SMDS_MeshNode* newNode = mesh->AddNode( midP.X(), midP.Y(), midP.Z() );
5751       newNodes.push_back( newNode );
5752
5753     }
5754     const SMDS_MeshNode* newNode = mesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
5755     newNodes.push_back( newNode );
5756
5757     centers.push_back( 0.5 * ( aV0x.XYZ() + aV1x.XYZ() ));
5758     centers.push_back( aV1x );
5759
5760     aPN0 = aPN1;
5761     aP0x = aP1x;
5762     aV0x = aV1x;
5763     aDT0x = aDT1x;
5764   }
5765
5766   // scale
5767   if ( !myScales.empty() )
5768   {
5769     gp_Trsf aTrsfScale;
5770     std::list<const SMDS_MeshNode*>::iterator node = newNodes.begin();
5771     for ( size_t i = !makeMediumNodes;
5772           i < myScales.size() && node != newNodes.end();
5773           i += ( 1 + !makeMediumNodes ), ++node )
5774     {
5775       aTrsfScale.SetScale( centers[ i ], myScales[ i ] );
5776       gp_Pnt aN = SMESH_NodeXYZ( *node );
5777       gp_Pnt aP = aN.Transformed( aTrsfScale );
5778       mesh->MoveNode( *node, aP.X(), aP.Y(), aP.Z() );
5779     }
5780   }
5781
5782   return myPathPoints.size() + makeMediumNodes * ( myPathPoints.size() - 2 );
5783 }
5784
5785 //=======================================================================
5786 //function : ExtrusionSweep
5787 //purpose  :
5788 //=======================================================================
5789
5790 SMESH_MeshEditor::PGroupIDs
5791 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet     theElems[2],
5792                                   const gp_Vec&        theStep,
5793                                   const int            theNbSteps,
5794                                   TTElemOfElemListMap& newElemsMap,
5795                                   const int            theFlags,
5796                                   const double         theTolerance)
5797 {
5798   std::list<double> dummy;
5799   ExtrusParam aParams( theStep, theNbSteps, dummy, dummy, 0,
5800                        theFlags, theTolerance );
5801   return ExtrusionSweep( theElems, aParams, newElemsMap );
5802 }
5803
5804 namespace
5805 {
5806
5807 //=======================================================================
5808 //function : getOriFactor
5809 //purpose  : Return -1 or 1 depending on if order of given nodes corresponds to
5810 //           edge curve orientation
5811 //=======================================================================
5812
5813   double getOriFactor( const TopoDS_Edge&   edge,
5814                        const SMDS_MeshNode* n1,
5815                        const SMDS_MeshNode* n2,
5816                        SMESH_MesherHelper&  helper)
5817   {
5818     double u1 = helper.GetNodeU( edge, n1, n2 );
5819     double u2 = helper.GetNodeU( edge, n2, n1 );
5820     return u1 < u2 ? 1. : -1.;
5821   }
5822 }
5823
5824 //=======================================================================
5825 //function : ExtrusionSweep
5826 //purpose  :
5827 //=======================================================================
5828
5829 SMESH_MeshEditor::PGroupIDs
5830 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet     theElemSets[2],
5831                                   ExtrusParam&         theParams,
5832                                   TTElemOfElemListMap& newElemsMap)
5833 {
5834   ClearLastCreated();
5835
5836   setElemsFirst( theElemSets );
5837   myLastCreatedElems.reserve( theElemSets[0].size() * theParams.NbSteps() );
5838   myLastCreatedNodes.reserve( theElemSets[1].size() * theParams.NbSteps() );
5839
5840   // source elements for each generated one
5841   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5842   srcElems.reserve( theElemSets[0].size() );
5843   srcNodes.reserve( theElemSets[1].size() );
5844
5845   const int nbSteps = theParams.NbSteps();
5846   theParams.SetElementsToUse( theElemSets[0], theElemSets[1] );
5847
5848   TNodeOfNodeListMap   mapNewNodes;
5849   TElemOfVecOfNnlmiMap mapElemNewNodes;
5850
5851   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5852                                      myMesh->NbFaces(ORDER_QUADRATIC) +
5853                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
5854   // loop on theElems
5855   TIDSortedElemSet::iterator itElem;
5856   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5857   {
5858     TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5859     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5860     {
5861       // check element type
5862       const SMDS_MeshElement* elem = *itElem;
5863       if ( !elem  || elem->GetType() == SMDSAbs_Volume )
5864         continue;
5865
5866       const size_t nbNodes = elem->NbNodes();
5867       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5868       newNodesItVec.reserve( nbNodes );
5869
5870       // loop on elem nodes
5871       SMDS_NodeIteratorPtr itN = elem->nodeIterator();
5872       while ( itN->more() )
5873       {
5874         // check if a node has been already sweeped
5875         const SMDS_MeshNode* node = itN->next();
5876         TNodeOfNodeListMap::iterator nIt =
5877           mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5878         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5879         if ( listNewNodes.empty() )
5880         {
5881           // make new nodes
5882
5883           // check if we are to create medium nodes between corner ones
5884           bool needMediumNodes = false;
5885           if ( isQuadraticMesh )
5886           {
5887             SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5888             while (it->more() && !needMediumNodes )
5889             {
5890               const SMDS_MeshElement* invElem = it->next();
5891               if ( invElem != elem && !theElems.count( invElem )) continue;
5892               needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5893               if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5894                 needMediumNodes = true;
5895             }
5896           }
5897           // create nodes for all steps
5898           if ( theParams.MakeNodes( GetMeshDS(), node, listNewNodes, needMediumNodes ))
5899           {
5900             list<const SMDS_MeshNode*>::iterator newNodesIt = listNewNodes.begin();
5901             for ( ; newNodesIt != listNewNodes.end(); ++newNodesIt )
5902             {
5903               myLastCreatedNodes.push_back( *newNodesIt );
5904               srcNodes.push_back( node );
5905             }
5906           }
5907           else
5908           {
5909             if ( theParams.ToMakeBoundary() )
5910             {
5911               GetMeshDS()->Modified();
5912               throw SALOME_Exception( SMESH_Comment("Can't extrude node #") << node->GetID() );
5913             }
5914             break; // newNodesItVec will be shorter than nbNodes
5915           }
5916         }
5917         newNodesItVec.push_back( nIt );
5918       }
5919       // make new elements
5920       if ( newNodesItVec.size() == nbNodes )
5921         sweepElement( elem, newNodesItVec, newElemsMap[elem], nbSteps, srcElems );
5922     }
5923   }
5924
5925   if ( theParams.ToMakeBoundary() ) {
5926     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], nbSteps, srcElems );
5927   }
5928   PGroupIDs newGroupIDs;
5929   if ( theParams.ToMakeGroups() )
5930     newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
5931
5932   return newGroupIDs;
5933 }
5934
5935 //=======================================================================
5936 //function : ExtrusionAlongTrack
5937 //purpose  :
5938 //=======================================================================
5939 SMESH_MeshEditor::Extrusion_Error
5940 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet     theElements[2],
5941                                        SMESH_Mesh*          theTrackMesh,
5942                                        SMDS_ElemIteratorPtr theTrackIterator,
5943                                        const SMDS_MeshNode* theN1,
5944                                        std::list<double>&   theAngles,
5945                                        const bool           theAngleVariation,
5946                                        std::list<double>&   theScales,
5947                                        const bool           theScaleVariation,
5948                                        const gp_Pnt*        theRefPoint,
5949                                        const bool           theMakeGroups)
5950 {
5951   ClearLastCreated();
5952
5953   // 1. Check data
5954   if ( theElements[0].empty() && theElements[1].empty() )
5955     return EXTR_NO_ELEMENTS;
5956
5957   ASSERT( theTrackMesh );
5958   if ( ! theTrackIterator || !theTrackIterator->more() )
5959     return EXTR_NO_ELEMENTS;
5960
5961   // 2. Get ordered nodes
5962   SMESH_MeshAlgos::TElemGroupVector branchEdges;
5963   SMESH_MeshAlgos::TNodeGroupVector branchNods;
5964   SMESH_MeshAlgos::Get1DBranches( theTrackIterator, branchEdges, branchNods, theN1 );
5965   if ( branchEdges.empty() )
5966     return EXTR_PATH_NOT_EDGE;
5967
5968   if ( branchEdges.size() > 1 )
5969     return EXTR_BAD_PATH_SHAPE;
5970
5971   std::vector< const SMDS_MeshNode* >&    pathNodes = branchNods[0];
5972   std::vector< const SMDS_MeshElement* >& pathEdges = branchEdges[0];
5973   if ( pathNodes[0] != theN1 && pathNodes[1] != theN1 )
5974     return EXTR_BAD_STARTING_NODE;
5975
5976   if ( theTrackMesh->NbEdges( ORDER_QUADRATIC ) > 0 )
5977   {
5978     // add medium nodes to pathNodes
5979     std::vector< const SMDS_MeshNode* >    pathNodes2;
5980     std::vector< const SMDS_MeshElement* > pathEdges2;
5981     pathNodes2.reserve( pathNodes.size() * 2 );
5982     pathEdges2.reserve( pathEdges.size() * 2 );
5983     for ( size_t i = 0; i < pathEdges.size(); ++i )
5984     {
5985       pathNodes2.push_back( pathNodes[i] );
5986       pathEdges2.push_back( pathEdges[i] );
5987       if ( pathEdges[i]->IsQuadratic() )
5988       {
5989         pathNodes2.push_back( pathEdges[i]->GetNode(2) );
5990         pathEdges2.push_back( pathEdges[i] );
5991       }
5992     }
5993     pathNodes2.push_back( pathNodes.back() );
5994     pathEdges.swap( pathEdges2 );
5995     pathNodes.swap( pathNodes2 );
5996   }
5997
5998   // 3. Get path data at pathNodes
5999
6000   std::vector< ExtrusParam::PathPoint > points( pathNodes.size() );
6001
6002   if ( theAngleVariation )
6003     linearAngleVariation( points.size()-1, theAngles );
6004   if ( theScaleVariation )
6005     linearScaleVariation( points.size()-1, theScales );
6006
6007   theAngles.push_front( 0 ); // for the 1st point that is not transformed
6008   std::list<double>::iterator angle = theAngles.begin();
6009
6010   SMESHDS_Mesh* pathMeshDS = theTrackMesh->GetMeshDS();
6011
6012   std::map< int, double > edgeID2OriFactor; // orientation of EDGEs
6013   std::map< int, double >::iterator id2factor;
6014   SMESH_MesherHelper pathHelper( *theTrackMesh );
6015   gp_Pnt p; gp_Vec tangent;
6016   const double tol2 = gp::Resolution() * gp::Resolution();
6017
6018   for ( size_t i = 0; i < pathNodes.size(); ++i )
6019   {
6020     ExtrusParam::PathPoint & point = points[ i ];
6021
6022     point.myPnt = SMESH_NodeXYZ( pathNodes[ i ]);
6023
6024     if ( angle != theAngles.end() )
6025       point.myAngle = *angle++;
6026
6027     tangent.SetCoord( 0,0,0 );
6028     const int          shapeID = pathNodes[ i ]->GetShapeID();
6029     const TopoDS_Shape&  shape = pathMeshDS->IndexToShape( shapeID );
6030     TopAbs_ShapeEnum shapeType = shape.IsNull() ? TopAbs_SHAPE : shape.ShapeType();
6031     switch ( shapeType )
6032     {
6033     case TopAbs_EDGE:
6034     {
6035       TopoDS_Edge edge = TopoDS::Edge( shape );
6036       id2factor = edgeID2OriFactor.insert( std::make_pair( shapeID, 0 )).first;
6037       if ( id2factor->second == 0 )
6038       {
6039         if ( i ) id2factor->second = getOriFactor( edge, pathNodes[i-1], pathNodes[i], pathHelper );
6040         else     id2factor->second = getOriFactor( edge, pathNodes[i], pathNodes[i+1], pathHelper );
6041       }
6042       double u = pathHelper.GetNodeU( edge, pathNodes[i] ), u0, u1;
6043       Handle(Geom_Curve) curve = BRep_Tool::Curve( edge, u0, u1 );
6044       curve->D1( u, p, tangent );
6045       tangent *= id2factor->second;
6046       break;
6047     }
6048     case TopAbs_VERTEX:
6049     {
6050       int nbEdges = 0;
6051       PShapeIteratorPtr shapeIt = pathHelper.GetAncestors( shape, *theTrackMesh, TopAbs_EDGE );
6052       while ( const TopoDS_Shape* edgePtr = shapeIt->next() )
6053       {
6054         int edgeID = pathMeshDS->ShapeToIndex( *edgePtr );
6055         for ( int di = -1; di <= 0; ++di )
6056         {
6057           size_t j = i + di;
6058           if ( j < pathEdges.size() && edgeID == pathEdges[ j ]->GetShapeID() )
6059           {
6060             TopoDS_Edge edge = TopoDS::Edge( *edgePtr );
6061             id2factor = edgeID2OriFactor.insert( std::make_pair( edgeID, 0 )).first;
6062             if ( id2factor->second == 0 )
6063             {
6064               if ( j < i )
6065                 id2factor->second = getOriFactor( edge, pathNodes[i-1], pathNodes[i], pathHelper );
6066               else
6067                 id2factor->second = getOriFactor( edge, pathNodes[i], pathNodes[i+1], pathHelper );
6068             }
6069             double u = pathHelper.GetNodeU( edge, pathNodes[i] ), u0, u1;
6070             Handle(Geom_Curve) curve = BRep_Tool::Curve( edge, u0, u1 );
6071             gp_Vec du;
6072             curve->D1( u, p, du );
6073             double size2 = du.SquareMagnitude();
6074             if ( du.SquareMagnitude() > tol2 )
6075             {
6076               tangent += du.Divided( Sqrt( size2 )) * id2factor->second;
6077               nbEdges++;
6078             }
6079             break;
6080           }
6081         }
6082       }
6083       if ( nbEdges > 0 )
6084         break;
6085     }
6086     // fall through
6087     default:
6088     {
6089       for ( int di = -1; di <= 1; di += 2 )
6090       {
6091         size_t j = i + di;
6092         if ( j < pathNodes.size() )
6093         {
6094           gp_Vec dir( point.myPnt, SMESH_NodeXYZ( pathNodes[ j ]));
6095           double size2 = dir.SquareMagnitude();
6096           if ( size2 > tol2 )
6097             tangent += dir.Divided( Sqrt( size2 )) * di;
6098         }
6099       }
6100     }
6101     } // switch ( shapeType )
6102
6103     if ( tangent.SquareMagnitude() < tol2 )
6104       return EXTR_CANT_GET_TANGENT;
6105
6106     point.myTgt = tangent;
6107
6108   } // loop on pathNodes
6109
6110
6111   ExtrusParam nodeMaker( points, theRefPoint, theScales, theMakeGroups );
6112   TTElemOfElemListMap newElemsMap;
6113
6114   ExtrusionSweep( theElements, nodeMaker, newElemsMap );
6115
6116   return EXTR_OK;
6117 }
6118
6119 //=======================================================================
6120 //function : linearAngleVariation
6121 //purpose  : spread values over nbSteps
6122 //=======================================================================
6123
6124 void SMESH_MeshEditor::linearAngleVariation(const int     nbSteps,
6125                                             list<double>& Angles)
6126 {
6127   int nbAngles = Angles.size();
6128   if( nbSteps > nbAngles && nbAngles > 0 )
6129   {
6130     vector<double> theAngles(nbAngles);
6131     theAngles.assign( Angles.begin(), Angles.end() );
6132
6133     list<double> res;
6134     double rAn2St = double( nbAngles ) / double( nbSteps );
6135     double angPrev = 0, angle;
6136     for ( int iSt = 0; iSt < nbSteps; ++iSt )
6137     {
6138       double angCur = rAn2St * ( iSt+1 );
6139       double angCurFloor  = floor( angCur );
6140       double angPrevFloor = floor( angPrev );
6141       if ( angPrevFloor == angCurFloor )
6142         angle = rAn2St * theAngles[ int( angCurFloor ) ];
6143       else {
6144         int iP = int( angPrevFloor );
6145         double angPrevCeil = ceil(angPrev);
6146         angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6147
6148         int iC = int( angCurFloor );
6149         if ( iC < nbAngles )
6150           angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6151
6152         iP = int( angPrevCeil );
6153         while ( iC-- > iP )
6154           angle += theAngles[ iC ];
6155       }
6156       res.push_back(angle);
6157       angPrev = angCur;
6158     }
6159     Angles.swap( res );
6160   }
6161 }
6162
6163 //=======================================================================
6164 //function : linearScaleVariation
6165 //purpose  : spread values over nbSteps 
6166 //=======================================================================
6167
6168 void SMESH_MeshEditor::linearScaleVariation(const int          theNbSteps,
6169                                             std::list<double>& theScales)
6170 {
6171   int nbScales = theScales.size();
6172   std::vector<double> myScales;
6173   myScales.reserve( theNbSteps );
6174   std::list<double>::const_iterator scale = theScales.begin();
6175   double prevScale = 1.0;
6176   for ( int iSc = 1; scale != theScales.end(); ++scale, ++iSc )
6177   {
6178     int      iStep = int( iSc / double( nbScales ) * theNbSteps + 0.5 );
6179     int    stDelta = Max( 1, iStep - myScales.size());
6180     double scDelta = ( *scale - prevScale ) / stDelta;
6181     for ( int iStep = 0; iStep < stDelta; ++iStep )
6182     {
6183       myScales.push_back( prevScale + scDelta );
6184       prevScale = myScales.back();
6185     }
6186     prevScale = *scale;
6187   }
6188   theScales.assign( myScales.begin(), myScales.end() );
6189 }
6190
6191 //================================================================================
6192 /*!
6193  * \brief Move or copy theElements applying theTrsf to their nodes
6194  *  \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6195  *  \param theTrsf - transformation to apply
6196  *  \param theCopy - if true, create translated copies of theElems
6197  *  \param theMakeGroups - if true and theCopy, create translated groups
6198  *  \param theTargetMesh - mesh to copy translated elements into
6199  *  \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6200  */
6201 //================================================================================
6202
6203 SMESH_MeshEditor::PGroupIDs
6204 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6205                              const gp_Trsf&     theTrsf,
6206                              const bool         theCopy,
6207                              const bool         theMakeGroups,
6208                              SMESH_Mesh*        theTargetMesh)
6209 {
6210   ClearLastCreated();
6211   myLastCreatedElems.reserve( theElems.size() );
6212
6213   bool needReverse = false;
6214   string groupPostfix;
6215   switch ( theTrsf.Form() ) {
6216   case gp_PntMirror:
6217     needReverse = true;
6218     groupPostfix = "mirrored";
6219     break;
6220   case gp_Ax1Mirror:
6221     groupPostfix = "mirrored";
6222     break;
6223   case gp_Ax2Mirror:
6224     needReverse = true;
6225     groupPostfix = "mirrored";
6226     break;
6227   case gp_Rotation:
6228     groupPostfix = "rotated";
6229     break;
6230   case gp_Translation:
6231     groupPostfix = "translated";
6232     break;
6233   case gp_Scale:
6234     groupPostfix = "scaled";
6235     break;
6236   case gp_CompoundTrsf: // different scale by axis
6237     groupPostfix = "scaled";
6238     break;
6239   default:
6240     needReverse = false;
6241     groupPostfix = "transformed";
6242   }
6243
6244   SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6245   SMESHDS_Mesh* aMesh    = GetMeshDS();
6246
6247   SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6248   SMESH_MeshEditor* editor = theTargetMesh ? & targetMeshEditor : theCopy ? this : 0;
6249   SMESH_MeshEditor::ElemFeatures elemType;
6250
6251   // map old node to new one
6252   TNodeNodeMap nodeMap;
6253
6254   // elements sharing moved nodes; those of them which have all
6255   // nodes mirrored but are not in theElems are to be reversed
6256   TIDSortedElemSet inverseElemSet;
6257
6258   // source elements for each generated one
6259   SMESH_SequenceOfElemPtr srcElems, srcNodes;
6260
6261   // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6262   TIDSortedElemSet orphanNode;
6263
6264   if ( theElems.empty() ) // transform the whole mesh
6265   {
6266     // add all elements
6267     SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6268     while ( eIt->more() ) theElems.insert( eIt->next() );
6269     // add orphan nodes
6270     SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6271     while ( nIt->more() )
6272     {
6273       const SMDS_MeshNode* node = nIt->next();
6274       if ( node->NbInverseElements() == 0)
6275         orphanNode.insert( node );
6276     }
6277   }
6278
6279   // loop on elements to transform nodes : first orphan nodes then elems
6280   TIDSortedElemSet::iterator itElem;
6281   TIDSortedElemSet *elements[] = { &orphanNode, &theElems };
6282   for (int i=0; i<2; i++)
6283     for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ )
6284     {
6285       const SMDS_MeshElement* elem = *itElem;
6286       if ( !elem )
6287         continue;
6288
6289       // loop on elem nodes
6290       double coord[3];
6291       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6292       while ( itN->more() )
6293       {
6294         const SMDS_MeshNode* node = cast2Node( itN->next() );
6295         // check if a node has been already transformed
6296         pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6297           nodeMap.insert( make_pair ( node, node ));
6298         if ( !n2n_isnew.second )
6299           continue;
6300
6301         node->GetXYZ( coord );
6302         theTrsf.Transforms( coord[0], coord[1], coord[2] );
6303         if ( theTargetMesh ) {
6304           const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6305           n2n_isnew.first->second = newNode;
6306           myLastCreatedNodes.push_back(newNode);
6307           srcNodes.push_back( node );
6308         }
6309         else if ( theCopy ) {
6310           const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6311           n2n_isnew.first->second = newNode;
6312           myLastCreatedNodes.push_back(newNode);
6313           srcNodes.push_back( node );
6314         }
6315         else {
6316           aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6317           // node position on shape becomes invalid
6318           const_cast< SMDS_MeshNode* > ( node )->SetPosition
6319             ( SMDS_SpacePosition::originSpacePosition() );
6320         }
6321
6322         // keep inverse elements
6323         if ( !theCopy && !theTargetMesh && needReverse ) {
6324           SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6325           while ( invElemIt->more() ) {
6326             const SMDS_MeshElement* iel = invElemIt->next();
6327             inverseElemSet.insert( iel );
6328           }
6329         }
6330       }
6331     } // loop on elems in { &orphanNode, &theElems };
6332
6333   // either create new elements or reverse mirrored ones
6334   if ( !theCopy && !needReverse && !theTargetMesh )
6335     return PGroupIDs();
6336
6337   theElems.insert( inverseElemSet.begin(),inverseElemSet.end() );
6338
6339   // Replicate or reverse elements
6340
6341   std::vector<int> iForw;
6342   vector<const SMDS_MeshNode*> nodes;
6343   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6344   {
6345     const SMDS_MeshElement* elem = *itElem;
6346     if ( !elem ) continue;
6347
6348     SMDSAbs_GeometryType geomType = elem->GetGeomType();
6349     size_t               nbNodes  = elem->NbNodes();
6350     if ( geomType == SMDSGeom_NONE ) continue; // node
6351
6352     nodes.resize( nbNodes );
6353
6354     if ( geomType == SMDSGeom_POLYHEDRA )  // ------------------ polyhedral volume
6355     {
6356       const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( elem );
6357       if ( !aPolyedre )
6358         continue;
6359       nodes.clear();
6360       bool allTransformed = true;
6361       int nbFaces = aPolyedre->NbFaces();
6362       for (int iface = 1; iface <= nbFaces && allTransformed; iface++)
6363       {
6364         int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6365         for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++)
6366         {
6367           const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6368           TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6369           if ( nodeMapIt == nodeMap.end() )
6370             allTransformed = false; // not all nodes transformed
6371           else
6372             nodes.push_back((*nodeMapIt).second);
6373         }
6374         if ( needReverse && allTransformed )
6375           std::reverse( nodes.end() - nbFaceNodes, nodes.end() );
6376       }
6377       if ( !allTransformed )
6378         continue; // not all nodes transformed
6379     }
6380     else // ----------------------- the rest element types
6381     {
6382       while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6383       const vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType(), nbNodes );
6384       const vector<int>&    i = needReverse ? iRev : iForw;
6385
6386       // find transformed nodes
6387       size_t iNode = 0;
6388       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6389       while ( itN->more() ) {
6390         const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
6391         TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6392         if ( nodeMapIt == nodeMap.end() )
6393           break; // not all nodes transformed
6394         nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
6395       }
6396       if ( iNode != nbNodes )
6397         continue; // not all nodes transformed
6398     }
6399
6400     if ( editor ) {
6401       // copy in this or a new mesh
6402       if ( editor->AddElement( nodes, elemType.Init( elem, /*basicOnly=*/false )))
6403         srcElems.push_back( elem );
6404     }
6405     else {
6406       // reverse element as it was reversed by transformation
6407       if ( nbNodes > 2 )
6408         aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
6409     }
6410
6411   } // loop on elements
6412
6413   if ( editor && editor != this )
6414     myLastCreatedElems.swap( editor->myLastCreatedElems );
6415
6416   PGroupIDs newGroupIDs;
6417
6418   if ( ( theMakeGroups && theCopy ) ||
6419        ( theMakeGroups && theTargetMesh ) )
6420     newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh, false );
6421
6422   return newGroupIDs;
6423 }
6424
6425 //================================================================================
6426 /*!
6427  * \brief Make an offset mesh from a source 2D mesh
6428  *  \param [in] theElements - source faces
6429  *  \param [in] theValue - offset value
6430  *  \param [out] theTgtMesh - a mesh to add offset elements to
6431  *  \param [in] theMakeGroups - to generate groups
6432  *  \return PGroupIDs - IDs of created groups. NULL means failure
6433  */
6434 //================================================================================
6435
6436 SMESH_MeshEditor::PGroupIDs SMESH_MeshEditor::Offset( TIDSortedElemSet & theElements,
6437                                                       const double       theValue,
6438                                                       SMESH_Mesh*        theTgtMesh,
6439                                                       const bool         theMakeGroups,
6440                                                       const bool         theCopyElements,
6441                                                       const bool         theFixSelfIntersection)
6442 {
6443   SMESHDS_Mesh*    meshDS = GetMeshDS();
6444   SMESHDS_Mesh* tgtMeshDS = theTgtMesh->GetMeshDS();
6445   SMESH_MeshEditor tgtEditor( theTgtMesh );
6446
6447   SMDS_ElemIteratorPtr eIt;
6448   if ( theElements.empty() ) eIt = meshDS->elementsIterator( SMDSAbs_Face );
6449   else                       eIt = SMESHUtils::elemSetIterator( theElements );
6450
6451   SMESH_MeshAlgos::TElemIntPairVec new2OldFaces;
6452   SMESH_MeshAlgos::TNodeIntPairVec new2OldNodes;
6453   std::unique_ptr< SMDS_Mesh > offsetMesh
6454     ( SMESH_MeshAlgos::MakeOffset( eIt, *meshDS, theValue,
6455                                    theFixSelfIntersection,
6456                                    new2OldFaces, new2OldNodes ));
6457   if ( offsetMesh->NbElements() == 0 )
6458     return PGroupIDs(); // MakeOffset() failed
6459
6460
6461   if ( theTgtMesh == myMesh && !theCopyElements )
6462   {
6463     // clear the source elements
6464     if ( theElements.empty() ) eIt = meshDS->elementsIterator( SMDSAbs_Face );
6465     else                       eIt = SMESHUtils::elemSetIterator( theElements );
6466     while ( eIt->more() )
6467       meshDS->RemoveFreeElement( eIt->next(), 0 );
6468   }
6469
6470   // offsetMesh->Modified();
6471   // offsetMesh->CompactMesh(); // make IDs start from 1
6472
6473   // source elements for each generated one
6474   SMESH_SequenceOfElemPtr srcElems, srcNodes;
6475   srcElems.reserve( new2OldFaces.size() );
6476   srcNodes.reserve( new2OldNodes.size() );
6477
6478   ClearLastCreated();
6479   myLastCreatedElems.reserve( new2OldFaces.size() );
6480   myLastCreatedNodes.reserve( new2OldNodes.size() );
6481
6482   // copy offsetMesh to theTgtMesh
6483
6484   int idShift = meshDS->MaxNodeID();
6485   for ( size_t i = 0; i < new2OldNodes.size(); ++i )
6486     if ( const SMDS_MeshNode* n = new2OldNodes[ i ].first )
6487     {
6488 #ifndef _DEBUG_
6489       if ( n->NbInverseElements() > 0 )
6490 #endif
6491       {
6492         const SMDS_MeshNode* n2 =
6493           tgtMeshDS->AddNodeWithID( n->X(), n->Y(), n->Z(), idShift + n->GetID() );
6494         myLastCreatedNodes.push_back( n2 );
6495         srcNodes.push_back( meshDS->FindNode( new2OldNodes[ i ].second ));
6496       }
6497     }
6498
6499   ElemFeatures elemType;
6500   for ( size_t i = 0; i < new2OldFaces.size(); ++i )
6501     if ( const SMDS_MeshElement* f = new2OldFaces[ i ].first )
6502     {
6503       elemType.Init( f );
6504       elemType.myNodes.clear();
6505       for ( SMDS_NodeIteratorPtr nIt = f->nodeIterator(); nIt->more(); )
6506       {
6507         const SMDS_MeshNode* n2 = nIt->next();
6508         elemType.myNodes.push_back( tgtMeshDS->FindNode( idShift + n2->GetID() ));
6509       }
6510       tgtEditor.AddElement( elemType.myNodes, elemType );
6511       srcElems.push_back( meshDS->FindElement( new2OldFaces[ i ].second ));
6512     }
6513
6514   myLastCreatedElems.swap( tgtEditor.myLastCreatedElems );
6515
6516   PGroupIDs newGroupIDs;
6517   if ( theMakeGroups )
6518     newGroupIDs = generateGroups( srcNodes, srcElems, "offset", theTgtMesh, false );
6519   else
6520     newGroupIDs.reset( new std::list< int > );
6521
6522   return newGroupIDs;
6523 }
6524
6525 //=======================================================================
6526 /*!
6527  * \brief Create groups of elements made during transformation
6528  *  \param nodeGens - nodes making corresponding myLastCreatedNodes
6529  *  \param elemGens - elements making corresponding myLastCreatedElems
6530  *  \param postfix - to push_back to names of new groups
6531  *  \param targetMesh - mesh to create groups in
6532  *  \param topPresent - is there are "top" elements that are created by sweeping
6533  */
6534 //=======================================================================
6535
6536 SMESH_MeshEditor::PGroupIDs
6537 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
6538                                  const SMESH_SequenceOfElemPtr& elemGens,
6539                                  const std::string&             postfix,
6540                                  SMESH_Mesh*                    targetMesh,
6541                                  const bool                     topPresent)
6542 {
6543   PGroupIDs newGroupIDs( new list<int> );
6544   SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6545
6546   // Sort existing groups by types and collect their names
6547
6548   // containers to store an old group and generated new ones;
6549   // 1st new group is for result elems of different type than a source one;
6550   // 2nd new group is for same type result elems ("top" group at extrusion)
6551   using boost::tuple;
6552   using boost::make_tuple;
6553   typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
6554   vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
6555   vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
6556   // group names
6557   set< string > groupNames;
6558
6559   SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
6560   if ( !groupIt->more() ) return newGroupIDs;
6561
6562   int newGroupID = mesh->GetGroupIds().back()+1;
6563   while ( groupIt->more() )
6564   {
6565     SMESH_Group * group = groupIt->next();
6566     if ( !group ) continue;
6567     SMESHDS_GroupBase* groupDS = group->GetGroupDS();
6568     if ( !groupDS || groupDS->IsEmpty() ) continue;
6569     groupNames.insert    ( group->GetName() );
6570     groupDS->SetStoreName( group->GetName() );
6571     const SMDSAbs_ElementType type = groupDS->GetType();
6572     SMESHDS_Group* newGroup    = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6573     SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6574     groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
6575     orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
6576   }
6577
6578   // Loop on nodes and elements to add them in new groups
6579
6580   vector< const SMDS_MeshElement* > resultElems;
6581   for ( int isNodes = 0; isNodes < 2; ++isNodes )
6582   {
6583     const SMESH_SequenceOfElemPtr& gens  = isNodes ? nodeGens : elemGens;
6584     const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
6585     if ( gens.size() != elems.size() )
6586       throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
6587
6588     // loop on created elements
6589     for (size_t iElem = 0; iElem < elems.size(); ++iElem )
6590     {
6591       const SMDS_MeshElement* sourceElem = gens[ iElem ];
6592       if ( !sourceElem ) {
6593         MESSAGE("generateGroups(): NULL source element");
6594         continue;
6595       }
6596       list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
6597       if ( groupsOldNew.empty() ) { // no groups of this type at all
6598         while ( iElem+1 < gens.size() && gens[ iElem+1 ] == sourceElem )
6599           ++iElem; // skip all elements made by sourceElem
6600         continue;
6601       }
6602       // collect all elements made by the iElem-th sourceElem
6603       resultElems.clear();
6604       if ( const SMDS_MeshElement* resElem = elems[ iElem ])
6605         if ( resElem != sourceElem )
6606           resultElems.push_back( resElem );
6607       while ( iElem+1 < gens.size() && gens[ iElem+1 ] == sourceElem )
6608         if ( const SMDS_MeshElement* resElem = elems[ ++iElem ])
6609           if ( resElem != sourceElem )
6610             resultElems.push_back( resElem );
6611
6612       const SMDS_MeshElement* topElem = 0;
6613       if ( isNodes ) // there must be a top element
6614       {
6615         topElem = resultElems.back();
6616         resultElems.pop_back();
6617       }
6618       else
6619       {
6620         vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
6621         for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
6622           if ( (*resElemIt)->GetType() == sourceElem->GetType() )
6623           {
6624             topElem = *resElemIt;
6625             *resElemIt = 0; // erase *resElemIt
6626             break;
6627           }
6628       }
6629       // add resultElems to groups originted from ones the sourceElem belongs to
6630       list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
6631       for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
6632       {
6633         SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
6634         if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
6635         {
6636           // fill in a new group
6637           SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
6638           vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
6639           for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
6640             if ( *resElemIt )
6641               newGroup.Add( *resElemIt );
6642
6643           // fill a "top" group
6644           if ( topElem )
6645           {
6646             SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
6647             newTopGroup.Add( topElem );
6648           }
6649         }
6650       }
6651     } // loop on created elements
6652   }// loop on nodes and elements
6653
6654   // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
6655
6656   list<int> topGrouIds;
6657   for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
6658   {
6659     SMESHDS_GroupBase* oldGroupDS =   orderedOldNewGroups[i]->get<0>();
6660     SMESHDS_Group*   newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
6661                                       orderedOldNewGroups[i]->get<2>() };
6662     for ( int is2nd = 0; is2nd < 2; ++is2nd )
6663     {
6664       SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
6665       if ( newGroupDS->IsEmpty() )
6666       {
6667         mesh->GetMeshDS()->RemoveGroup( newGroupDS );
6668       }
6669       else
6670       {
6671         // set group type
6672         newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
6673
6674         // make a name
6675         const bool isTop = ( topPresent &&
6676                              newGroupDS->GetType() == oldGroupDS->GetType() &&
6677                              is2nd );
6678
6679         string name = oldGroupDS->GetStoreName();
6680         { // remove trailing whitespaces (issue 22599)
6681           size_t size = name.size();
6682           while ( size > 1 && isspace( name[ size-1 ]))
6683             --size;
6684           if ( size != name.size() )
6685           {
6686             name.resize( size );
6687             oldGroupDS->SetStoreName( name.c_str() );
6688           }
6689         }
6690         if ( !targetMesh ) {
6691           string suffix = ( isTop ? "top": postfix.c_str() );
6692           name += "_";
6693           name += suffix;
6694           int nb = 1;
6695           while ( !groupNames.insert( name ).second ) // name exists
6696             name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
6697         }
6698         else if ( isTop ) {
6699           name += "_top";
6700         }
6701         newGroupDS->SetStoreName( name.c_str() );
6702
6703         // make a SMESH_Groups
6704         mesh->AddGroup( newGroupDS );
6705         if ( isTop )
6706           topGrouIds.push_back( newGroupDS->GetID() );
6707         else
6708           newGroupIDs->push_back( newGroupDS->GetID() );
6709       }
6710     }
6711   }
6712   newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
6713
6714   return newGroupIDs;
6715 }
6716
6717 //================================================================================
6718 /*!
6719  *  * \brief Return list of group of nodes close to each other within theTolerance
6720  *  *        Search among theNodes or in the whole mesh if theNodes is empty using
6721  *  *        an Octree algorithm
6722  *  \param [in,out] theNodes - the nodes to treat
6723  *  \param [in]     theTolerance - the tolerance
6724  *  \param [out]    theGroupsOfNodes - the result groups of coincident nodes
6725  *  \param [in]     theSeparateCornersAndMedium - if \c true, in quadratic mesh puts 
6726  *         corner and medium nodes in separate groups
6727  */
6728 //================================================================================
6729
6730 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet &   theNodes,
6731                                             const double         theTolerance,
6732                                             TListOfListOfNodes & theGroupsOfNodes,
6733                                             bool                 theSeparateCornersAndMedium)
6734 {
6735   ClearLastCreated();
6736
6737   if ( myMesh->NbEdges  ( ORDER_QUADRATIC ) +
6738        myMesh->NbFaces  ( ORDER_QUADRATIC ) +
6739        myMesh->NbVolumes( ORDER_QUADRATIC ) == 0 )
6740     theSeparateCornersAndMedium = false;
6741
6742   TIDSortedNodeSet& corners = theNodes;
6743   TIDSortedNodeSet  medium;
6744
6745   if ( theNodes.empty() ) // get all nodes in the mesh
6746   {
6747     TIDSortedNodeSet* nodes[2] = { &corners, &medium };
6748     SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator();
6749     if ( theSeparateCornersAndMedium )
6750       while ( nIt->more() )
6751       {
6752         const SMDS_MeshNode* n = nIt->next();
6753         TIDSortedNodeSet* & nodeSet = nodes[ SMESH_MesherHelper::IsMedium( n )];
6754         nodeSet->insert( nodeSet->end(), n );
6755       }
6756     else
6757       while ( nIt->more() )
6758         theNodes.insert( theNodes.end(), nIt->next() );
6759   }
6760   else if ( theSeparateCornersAndMedium ) // separate corners from medium nodes
6761   {
6762     TIDSortedNodeSet::iterator nIt = corners.begin();
6763     while ( nIt != corners.end() )
6764       if ( SMESH_MesherHelper::IsMedium( *nIt ))
6765       {
6766         medium.insert( medium.end(), *nIt );
6767         corners.erase( nIt++ );
6768       }
6769       else
6770       {
6771         ++nIt;
6772       }
6773   }
6774
6775   if ( !corners.empty() )
6776     SMESH_OctreeNode::FindCoincidentNodes ( corners, &theGroupsOfNodes, theTolerance );
6777   if ( !medium.empty() )
6778     SMESH_OctreeNode::FindCoincidentNodes ( medium, &theGroupsOfNodes, theTolerance );
6779 }
6780
6781 //=======================================================================
6782 //function : SimplifyFace
6783 //purpose  : split a chain of nodes into several closed chains
6784 //=======================================================================
6785
6786 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
6787                                     vector<const SMDS_MeshNode *>&       poly_nodes,
6788                                     vector<int>&                         quantities) const
6789 {
6790   int nbNodes = faceNodes.size();
6791   while ( faceNodes[ 0 ] == faceNodes[ nbNodes-1 ] && nbNodes > 2 )
6792     --nbNodes;
6793   if ( nbNodes < 3 )
6794     return 0;
6795   size_t prevNbQuant = quantities.size();
6796
6797   vector< const SMDS_MeshNode* > simpleNodes; simpleNodes.reserve( nbNodes );
6798   map< const SMDS_MeshNode*, int > nodeIndices; // indices within simpleNodes
6799   map< const SMDS_MeshNode*, int >::iterator nInd;
6800
6801   nodeIndices.insert( make_pair( faceNodes[0], 0 ));
6802   simpleNodes.push_back( faceNodes[0] );
6803   for ( int iCur = 1; iCur < nbNodes; iCur++ )
6804   {
6805     if ( faceNodes[ iCur ] != simpleNodes.back() )
6806     {
6807       int index = simpleNodes.size();
6808       nInd = nodeIndices.insert( make_pair( faceNodes[ iCur ], index )).first;
6809       int prevIndex = nInd->second;
6810       if ( prevIndex < index )
6811       {
6812         // a sub-loop found
6813         int loopLen = index - prevIndex;
6814         if ( loopLen > 2 )
6815         {
6816           // store the sub-loop
6817           quantities.push_back( loopLen );
6818           for ( int i = prevIndex; i < index; i++ )
6819             poly_nodes.push_back( simpleNodes[ i ]);
6820         }
6821         simpleNodes.resize( prevIndex+1 );
6822       }
6823       else
6824       {
6825         simpleNodes.push_back( faceNodes[ iCur ]);
6826       }
6827     }
6828   }
6829
6830   if ( simpleNodes.size() > 2 )
6831   {
6832     quantities.push_back( simpleNodes.size() );
6833     poly_nodes.insert ( poly_nodes.end(), simpleNodes.begin(), simpleNodes.end() );
6834   }
6835
6836   return quantities.size() - prevNbQuant;
6837 }
6838
6839 //=======================================================================
6840 //function : MergeNodes
6841 //purpose  : In each group, the cdr of nodes are substituted by the first one
6842 //           in all elements.
6843 //=======================================================================
6844
6845 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes,
6846                                    const bool           theAvoidMakingHoles)
6847 {
6848   ClearLastCreated();
6849
6850   SMESHDS_Mesh* mesh = GetMeshDS();
6851
6852   TNodeNodeMap nodeNodeMap; // node to replace - new node
6853   set<const SMDS_MeshElement*> elems; // all elements with changed nodes
6854   list< int > rmElemIds, rmNodeIds;
6855   vector< ElemFeatures > newElemDefs;
6856
6857   // Fill nodeNodeMap and elems
6858
6859   TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
6860   for ( ; grIt != theGroupsOfNodes.end(); grIt++ )
6861   {
6862     list<const SMDS_MeshNode*>& nodes = *grIt;
6863     list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
6864     const SMDS_MeshNode* nToKeep = *nIt;
6865     for ( ++nIt; nIt != nodes.end(); nIt++ )
6866     {
6867       const SMDS_MeshNode* nToRemove = *nIt;
6868       nodeNodeMap.insert( make_pair( nToRemove, nToKeep ));
6869       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
6870       while ( invElemIt->more() ) {
6871         const SMDS_MeshElement* elem = invElemIt->next();
6872         elems.insert(elem);
6873       }
6874     }
6875   }
6876
6877   // Apply recursive replacements (BUG 0020185)
6878   TNodeNodeMap::iterator nnIt = nodeNodeMap.begin();
6879   for ( ; nnIt != nodeNodeMap.end(); ++nnIt )
6880   {
6881     const SMDS_MeshNode* nToKeep = nnIt->second;
6882     TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( nToKeep );
6883     while ( nnIt_i != nodeNodeMap.end() && nnIt_i->second != nnIt->second )
6884     {
6885       nToKeep = nnIt_i->second;
6886       nnIt->second = nToKeep;
6887       nnIt_i = nodeNodeMap.find( nToKeep );
6888     }
6889   }
6890
6891   if ( theAvoidMakingHoles )
6892   {
6893     // find elements whose topology changes
6894
6895     vector<const SMDS_MeshElement*> pbElems;
6896     set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
6897     for ( ; eIt != elems.end(); ++eIt )
6898     {
6899       const SMDS_MeshElement* elem = *eIt;
6900       SMDS_ElemIteratorPtr     itN = elem->nodesIterator();
6901       while ( itN->more() )
6902       {
6903         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
6904         TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
6905         if ( nnIt != nodeNodeMap.end() && elem->GetNodeIndex( nnIt->second ) >= 0 )
6906         {
6907           // several nodes of elem stick
6908           pbElems.push_back( elem );
6909           break;
6910         }
6911       }
6912     }
6913     // exclude from merge nodes causing spoiling element
6914     for ( size_t iLoop = 0; iLoop < pbElems.size(); ++iLoop ) // avoid infinite cycle
6915     {
6916       bool nodesExcluded = false;
6917       for ( size_t i = 0; i < pbElems.size(); ++i )
6918       {
6919         size_t prevNbMergeNodes = nodeNodeMap.size();
6920         if ( !applyMerge( pbElems[i], newElemDefs, nodeNodeMap, /*noHoles=*/true ) &&
6921              prevNbMergeNodes < nodeNodeMap.size() )
6922           nodesExcluded = true;
6923       }
6924       if ( !nodesExcluded )
6925         break;
6926     }
6927   }
6928
6929   for ( nnIt = nodeNodeMap.begin(); nnIt != nodeNodeMap.end(); ++nnIt )
6930   {
6931     const SMDS_MeshNode* nToRemove = nnIt->first;
6932     const SMDS_MeshNode* nToKeep   = nnIt->second;
6933     if ( nToRemove != nToKeep )
6934     {
6935       rmNodeIds.push_back( nToRemove->GetID() );
6936       AddToSameGroups( nToKeep, nToRemove, mesh );
6937       // set _alwaysComputed to a sub-mesh of VERTEX to enable further mesh computing
6938       // w/o creating node in place of merged ones.
6939       SMDS_PositionPtr pos = nToRemove->GetPosition();
6940       if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
6941         if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
6942           sm->SetIsAlwaysComputed( true );
6943     }
6944   }
6945
6946   // Change element nodes or remove an element
6947
6948   set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
6949   for ( ; eIt != elems.end(); eIt++ )
6950   {
6951     const SMDS_MeshElement* elem = *eIt;
6952     SMESHDS_SubMesh*          sm = mesh->MeshElements( elem->getshapeId() );
6953
6954     bool keepElem = applyMerge( elem, newElemDefs, nodeNodeMap, /*noHoles=*/false );
6955     if ( !keepElem )
6956       rmElemIds.push_back( elem->GetID() );
6957
6958     for ( size_t i = 0; i < newElemDefs.size(); ++i )
6959     {
6960       if ( i > 0 || !mesh->ChangeElementNodes( elem,
6961                                                & newElemDefs[i].myNodes[0],
6962                                                newElemDefs[i].myNodes.size() ))
6963       {
6964         if ( i == 0 )
6965         {
6966           newElemDefs[i].SetID( elem->GetID() );
6967           mesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
6968           if ( !keepElem ) rmElemIds.pop_back();
6969         }
6970         else
6971         {
6972           newElemDefs[i].SetID( -1 );
6973         }
6974         SMDS_MeshElement* newElem = this->AddElement( newElemDefs[i].myNodes, newElemDefs[i] );
6975         if ( sm && newElem )
6976           sm->AddElement( newElem );
6977         if ( elem != newElem )
6978           ReplaceElemInGroups( elem, newElem, mesh );
6979       }
6980     }
6981   }
6982
6983   // Remove bad elements, then equal nodes (order important)
6984   Remove( rmElemIds, /*isNodes=*/false );
6985   Remove( rmNodeIds, /*isNodes=*/true );
6986
6987   return;
6988 }
6989
6990 //=======================================================================
6991 //function : applyMerge
6992 //purpose  : Compute new connectivity of an element after merging nodes
6993 //  \param [in] elems - the element
6994 //  \param [out] newElemDefs - definition(s) of result element(s)
6995 //  \param [inout] nodeNodeMap - nodes to merge
6996 //  \param [in] avoidMakingHoles - if true and and the element becomes invalid
6997 //              after merging (but not degenerated), removes nodes causing
6998 //              the invalidity from \a nodeNodeMap.
6999 //  \return bool - true if the element should be removed
7000 //=======================================================================
7001
7002 bool SMESH_MeshEditor::applyMerge( const SMDS_MeshElement* elem,
7003                                    vector< ElemFeatures >& newElemDefs,
7004                                    TNodeNodeMap&           nodeNodeMap,
7005                                    const bool              avoidMakingHoles )
7006 {
7007   bool toRemove = false; // to remove elem
7008   int nbResElems = 1;    // nb new elements
7009
7010   newElemDefs.resize(nbResElems);
7011   newElemDefs[0].Init( elem );
7012   newElemDefs[0].myNodes.clear();
7013
7014   set<const SMDS_MeshNode*> nodeSet;
7015   vector< const SMDS_MeshNode*>   curNodes;
7016   vector< const SMDS_MeshNode*> & uniqueNodes = newElemDefs[0].myNodes;
7017   vector<int> iRepl;
7018
7019   const        int  nbNodes = elem->NbNodes();
7020   SMDSAbs_EntityType entity = elem->GetEntityType();
7021
7022   curNodes.resize( nbNodes );
7023   uniqueNodes.resize( nbNodes );
7024   iRepl.resize( nbNodes );
7025   int iUnique = 0, iCur = 0, nbRepl = 0;
7026
7027   // Get new seq of nodes
7028
7029   SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7030   while ( itN->more() )
7031   {
7032     const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7033
7034     TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7035     if ( nnIt != nodeNodeMap.end() ) {
7036       n = (*nnIt).second;
7037     }
7038     curNodes[ iCur ] = n;
7039     bool isUnique = nodeSet.insert( n ).second;
7040     if ( isUnique )
7041       uniqueNodes[ iUnique++ ] = n;
7042     else
7043       iRepl[ nbRepl++ ] = iCur;
7044     iCur++;
7045   }
7046
7047   // Analyse element topology after replacement
7048
7049   int nbUniqueNodes = nodeSet.size();
7050   if ( nbNodes != nbUniqueNodes ) // some nodes stick
7051   {
7052     toRemove = true;
7053     nbResElems = 0;
7054
7055     if ( newElemDefs[0].myIsQuad && newElemDefs[0].myType == SMDSAbs_Face && nbNodes > 6 )
7056     {
7057       // if corner nodes stick, remove medium nodes between them from uniqueNodes
7058       int nbCorners = nbNodes / 2;
7059       for ( int iCur = 0; iCur < nbCorners; ++iCur )
7060       {
7061         int iNext = ( iCur + 1 ) % nbCorners;
7062         if ( curNodes[ iCur ] == curNodes[ iNext ] ) // corners stick
7063         {
7064           int iMedium = iCur + nbCorners;
7065           vector< const SMDS_MeshNode* >::iterator i =
7066             std::find( uniqueNodes.begin() + nbCorners - nbRepl,
7067                        uniqueNodes.end(),
7068                        curNodes[ iMedium ]);
7069           if ( i != uniqueNodes.end() )
7070           {
7071             --nbUniqueNodes;
7072             for ( ; i+1 != uniqueNodes.end(); ++i )
7073               *i = *(i+1);
7074           }
7075         }
7076       }
7077     }
7078
7079     switch ( entity )
7080     {
7081     case SMDSEntity_Polygon:
7082     case SMDSEntity_Quad_Polygon: // Polygon
7083     {
7084       ElemFeatures* elemType = & newElemDefs[0];
7085       const bool isQuad = elemType->myIsQuad;
7086       if ( isQuad )
7087         SMDS_MeshCell::applyInterlace // interlace medium and corner nodes
7088           ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon, nbNodes ), curNodes );
7089
7090       // a polygon can divide into several elements
7091       vector<const SMDS_MeshNode *> polygons_nodes;
7092       vector<int> quantities;
7093       nbResElems = SimplifyFace( curNodes, polygons_nodes, quantities );
7094       newElemDefs.resize( nbResElems );
7095       for ( int inode = 0, iface = 0; iface < nbResElems; iface++ )
7096       {
7097         ElemFeatures* elemType = & newElemDefs[iface];
7098         if ( iface ) elemType->Init( elem );
7099
7100         vector<const SMDS_MeshNode *>& face_nodes = elemType->myNodes;
7101         int nbNewNodes = quantities[iface];
7102         face_nodes.assign( polygons_nodes.begin() + inode,
7103                            polygons_nodes.begin() + inode + nbNewNodes );
7104         inode += nbNewNodes;
7105         if ( isQuad ) // check if a result elem is a valid quadratic polygon
7106         {
7107           bool isValid = ( nbNewNodes % 2 == 0 );
7108           for ( int i = 0; i < nbNewNodes && isValid; ++i )
7109             isValid = ( elem->IsMediumNode( face_nodes[i]) == bool( i % 2 ));
7110           elemType->SetQuad( isValid );
7111           if ( isValid ) // put medium nodes after corners
7112             SMDS_MeshCell::applyInterlaceRev
7113               ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon,
7114                                                     nbNewNodes ), face_nodes );
7115         }
7116         elemType->SetPoly(( nbNewNodes / ( elemType->myIsQuad + 1 ) > 4 ));
7117       }
7118       nbUniqueNodes = newElemDefs[0].myNodes.size();
7119       break;
7120     } // Polygon
7121
7122     case SMDSEntity_Polyhedra: // Polyhedral volume
7123     {
7124       if ( nbUniqueNodes >= 4 )
7125       {
7126         // each face has to be analyzed in order to check volume validity
7127         if ( const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( elem ))
7128         {
7129           int nbFaces = aPolyedre->NbFaces();
7130
7131           vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7132           vector<int>                  & quantities = newElemDefs[0].myPolyhedQuantities;
7133           vector<const SMDS_MeshNode *>  faceNodes;
7134           poly_nodes.clear();
7135           quantities.clear();
7136
7137           for (int iface = 1; iface <= nbFaces; iface++)
7138           {
7139             int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7140             faceNodes.resize( nbFaceNodes );
7141             for (int inode = 1; inode <= nbFaceNodes; inode++)
7142             {
7143               const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7144               TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7145               if ( nnIt != nodeNodeMap.end() ) // faceNode sticks
7146                 faceNode = (*nnIt).second;
7147               faceNodes[inode - 1] = faceNode;
7148             }
7149             SimplifyFace(faceNodes, poly_nodes, quantities);
7150           }
7151
7152           if ( quantities.size() > 3 )
7153           {
7154             // TODO: remove coincident faces
7155             nbResElems = 1;
7156             nbUniqueNodes = newElemDefs[0].myNodes.size();
7157           }
7158         }
7159       }
7160     }
7161     break;
7162
7163     // Regular elements
7164     // TODO not all the possible cases are solved. Find something more generic?
7165     case SMDSEntity_Edge: //////// EDGE
7166     case SMDSEntity_Triangle: //// TRIANGLE
7167     case SMDSEntity_Quad_Triangle:
7168     case SMDSEntity_Tetra:
7169     case SMDSEntity_Quad_Tetra: // TETRAHEDRON
7170     {
7171       break;
7172     }
7173     case SMDSEntity_Quad_Edge:
7174     {
7175       break;
7176     }
7177     case SMDSEntity_Quadrangle: //////////////////////////////////// QUADRANGLE
7178     {
7179       if ( nbUniqueNodes < 3 )
7180         toRemove = true;
7181       else if ( nbRepl == 1 && curNodes[ iRepl[0]] == curNodes[( iRepl[0]+2 )%4 ])
7182         toRemove = true; // opposite nodes stick
7183       else
7184         toRemove = false;
7185       break;
7186     }
7187     case SMDSEntity_Quad_Quadrangle: // Quadratic QUADRANGLE
7188     {
7189       //   1    5    2
7190       //    +---+---+
7191       //    |       |
7192       //   4+       +6
7193       //    |       |
7194       //    +---+---+
7195       //   0    7    3
7196       if ( nbUniqueNodes == 6 &&
7197            iRepl[0] < 4       &&
7198            ( nbRepl == 1 || iRepl[1] >= 4 ))
7199       {
7200         toRemove = false;
7201       }
7202       break;
7203     }
7204     case SMDSEntity_BiQuad_Quadrangle: // Bi-Quadratic QUADRANGLE
7205     {
7206       //   1    5    2
7207       //    +---+---+
7208       //    |       |
7209       //   4+  8+   +6
7210       //    |       |
7211       //    +---+---+
7212       //   0    7    3
7213       if ( nbUniqueNodes == 7 &&
7214            iRepl[0] < 4       &&
7215            ( nbRepl == 1 || iRepl[1] != 8 ))
7216       {
7217         toRemove = false;
7218       }
7219       break;
7220     }
7221     case SMDSEntity_Penta: ///////////////////////////////////// PENTAHEDRON
7222     {
7223       if ( nbUniqueNodes == 4 ) {
7224         // ---------------------------------> tetrahedron
7225         if ( curNodes[3] == curNodes[4] &&
7226              curNodes[3] == curNodes[5] ) {
7227           // top nodes stick
7228           toRemove = false;
7229         }
7230         else if ( curNodes[0] == curNodes[1] &&
7231                   curNodes[0] == curNodes[2] ) {
7232           // bottom nodes stick: set a top before
7233           uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7234           uniqueNodes[ 0 ] = curNodes [ 5 ];
7235           uniqueNodes[ 1 ] = curNodes [ 4 ];
7236           uniqueNodes[ 2 ] = curNodes [ 3 ];
7237           toRemove = false;
7238         }
7239         else if (( curNodes[0] == curNodes[3] ) +
7240                  ( curNodes[1] == curNodes[4] ) +
7241                  ( curNodes[2] == curNodes[5] ) == 2 ) {
7242           // a lateral face turns into a line
7243           toRemove = false;
7244         }
7245       }
7246       else if ( nbUniqueNodes == 5 ) {
7247         // PENTAHEDRON --------------------> pyramid
7248         if ( curNodes[0] == curNodes[3] )
7249         {
7250           uniqueNodes[ 0 ] = curNodes[ 1 ];
7251           uniqueNodes[ 1 ] = curNodes[ 4 ];
7252           uniqueNodes[ 2 ] = curNodes[ 5 ];
7253           uniqueNodes[ 3 ] = curNodes[ 2 ];
7254           uniqueNodes[ 4 ] = curNodes[ 0 ];
7255           toRemove = false;
7256         }
7257         if ( curNodes[1] == curNodes[4] )
7258         {
7259           uniqueNodes[ 0 ] = curNodes[ 0 ];
7260           uniqueNodes[ 1 ] = curNodes[ 2 ];
7261           uniqueNodes[ 2 ] = curNodes[ 5 ];
7262           uniqueNodes[ 3 ] = curNodes[ 3 ];
7263           uniqueNodes[ 4 ] = curNodes[ 1 ];
7264           toRemove = false;
7265         }
7266         if ( curNodes[2] == curNodes[5] )
7267         {
7268           uniqueNodes[ 0 ] = curNodes[ 0 ];
7269           uniqueNodes[ 1 ] = curNodes[ 3 ];
7270           uniqueNodes[ 2 ] = curNodes[ 4 ];
7271           uniqueNodes[ 3 ] = curNodes[ 1 ];
7272           uniqueNodes[ 4 ] = curNodes[ 2 ];
7273           toRemove = false;
7274         }
7275       }
7276       break;
7277     }
7278     case SMDSEntity_Hexa:
7279     {
7280       //////////////////////////////////// HEXAHEDRON
7281       SMDS_VolumeTool hexa (elem);
7282       hexa.SetExternalNormal();
7283       if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7284         //////////////////////// HEX ---> tetrahedron
7285         for ( int iFace = 0; iFace < 6; iFace++ ) {
7286           const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7287           if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7288               curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7289               curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7290             // one face turns into a point ...
7291             int  pickInd = ind[ 0 ];
7292             int iOppFace = hexa.GetOppFaceIndex( iFace );
7293             ind = hexa.GetFaceNodesIndices( iOppFace );
7294             int nbStick = 0;
7295             uniqueNodes.clear();
7296             for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7297               if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7298                 nbStick++;
7299               else
7300                 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7301             }
7302             if ( nbStick == 1 ) {
7303               // ... and the opposite one - into a triangle.
7304               // set a top node
7305               uniqueNodes.push_back( curNodes[ pickInd ]);
7306               toRemove = false;
7307             }
7308             break;
7309           }
7310         }
7311       }
7312       else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7313         //////////////////////// HEX ---> prism
7314         int nbTria = 0, iTria[3];
7315         const int *ind; // indices of face nodes
7316         // look for triangular faces
7317         for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7318           ind = hexa.GetFaceNodesIndices( iFace );
7319           TIDSortedNodeSet faceNodes;
7320           for ( iCur = 0; iCur < 4; iCur++ )
7321             faceNodes.insert( curNodes[ind[iCur]] );
7322           if ( faceNodes.size() == 3 )
7323             iTria[ nbTria++ ] = iFace;
7324         }
7325         // check if triangles are opposite
7326         if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7327         {
7328           // set nodes of the bottom triangle
7329           ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7330           vector<int> indB;
7331           for ( iCur = 0; iCur < 4; iCur++ )
7332             if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7333               indB.push_back( ind[iCur] );
7334           if ( !hexa.IsForward() )
7335             std::swap( indB[0], indB[2] );
7336           for ( iCur = 0; iCur < 3; iCur++ )
7337             uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7338           // set nodes of the top triangle
7339           const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7340           for ( iCur = 0; iCur < 3; ++iCur )
7341             for ( int j = 0; j < 4; ++j )
7342               if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7343               {
7344                 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7345                 break;
7346               }
7347           toRemove = false;
7348           break;
7349         }
7350       }
7351       else if (nbUniqueNodes == 5 && nbRepl == 3 ) {
7352         //////////////////// HEXAHEDRON ---> pyramid
7353         for ( int iFace = 0; iFace < 6; iFace++ ) {
7354           const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7355           if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7356               curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7357               curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7358             // one face turns into a point ...
7359             int iOppFace = hexa.GetOppFaceIndex( iFace );
7360             ind = hexa.GetFaceNodesIndices( iOppFace );
7361             uniqueNodes.clear();
7362             for ( iCur = 0; iCur < 4; iCur++ ) {
7363               if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7364                 break;
7365               else
7366                 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7367             }
7368             if ( uniqueNodes.size() == 4 ) {
7369               // ... and the opposite one is a quadrangle
7370               // set a top node
7371               const int* indTop = hexa.GetFaceNodesIndices( iFace );
7372               uniqueNodes.push_back( curNodes[indTop[ 0 ]]);
7373               toRemove = false;
7374             }
7375             break;
7376           }
7377         }
7378       }
7379
7380       if ( toRemove && nbUniqueNodes > 4 ) {
7381         ////////////////// HEXAHEDRON ---> polyhedron
7382         hexa.SetExternalNormal();
7383         vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7384         vector<int>                  & quantities = newElemDefs[0].myPolyhedQuantities;
7385         poly_nodes.reserve( 6 * 4 ); poly_nodes.clear();
7386         quantities.reserve( 6 );     quantities.clear();
7387         for ( int iFace = 0; iFace < 6; iFace++ )
7388         {
7389           const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7390           if ( curNodes[ind[0]] == curNodes[ind[2]] ||
7391                curNodes[ind[1]] == curNodes[ind[3]] )
7392           {
7393             quantities.clear();
7394             break; // opposite nodes stick
7395           }
7396           nodeSet.clear();
7397           for ( iCur = 0; iCur < 4; iCur++ )
7398           {
7399             if ( nodeSet.insert( curNodes[ind[ iCur ]] ).second )
7400               poly_nodes.push_back( curNodes[ind[ iCur ]]);
7401           }
7402           if ( nodeSet.size() < 3 )
7403             poly_nodes.resize( poly_nodes.size() - nodeSet.size() );
7404           else
7405             quantities.push_back( nodeSet.size() );
7406         }
7407         if ( quantities.size() >= 4 )
7408         {
7409           nbResElems = 1;
7410           nbUniqueNodes = poly_nodes.size();
7411           newElemDefs[0].SetPoly(true);
7412         }
7413       }
7414       break;
7415     } // case HEXAHEDRON
7416
7417     default:
7418       toRemove = true;
7419
7420     } // switch ( entity )
7421
7422     if ( toRemove && nbResElems == 0 && avoidMakingHoles )
7423     {
7424       // erase from nodeNodeMap nodes whose merge spoils elem
7425       vector< const SMDS_MeshNode* > noMergeNodes;
7426       SMESH_MeshAlgos::DeMerge( elem, curNodes, noMergeNodes );
7427       for ( size_t i = 0; i < noMergeNodes.size(); ++i )
7428         nodeNodeMap.erase( noMergeNodes[i] );
7429     }
7430     
7431   } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7432
7433   uniqueNodes.resize( nbUniqueNodes );
7434
7435   if ( !toRemove && nbResElems == 0 )
7436     nbResElems = 1;
7437
7438   newElemDefs.resize( nbResElems );
7439
7440   return !toRemove;
7441 }
7442
7443
7444 // ========================================================
7445 // class   : ComparableElement
7446 // purpose : allow comparing elements basing on their nodes
7447 // ========================================================
7448
7449 class ComparableElement : public boost::container::flat_set< int >
7450 {
7451   typedef boost::container::flat_set< int >  int_set;
7452
7453   const SMDS_MeshElement* myElem;
7454   int                     mySumID;
7455   mutable int             myGroupID;
7456
7457 public:
7458
7459   ComparableElement( const SMDS_MeshElement* theElem ):
7460     myElem ( theElem ), mySumID( 0 ), myGroupID( -1 )
7461   {
7462     this->reserve( theElem->NbNodes() );
7463     for ( SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator(); nodeIt->more(); )
7464     {
7465       int id = nodeIt->next()->GetID();
7466       mySumID += id;
7467       this->insert( id );
7468     }
7469   }
7470
7471   const SMDS_MeshElement* GetElem() const { return myElem; }
7472
7473   int& GroupID() const { return myGroupID; }
7474   //int& GroupID() const { return const_cast< int& >( myGroupID ); }
7475
7476   ComparableElement( const ComparableElement& theSource ) // move copy
7477     : int_set()
7478   {
7479     ComparableElement& src = const_cast< ComparableElement& >( theSource );
7480     (int_set&) (*this ) = std::move( src );
7481     myElem    = src.myElem;
7482     mySumID   = src.mySumID;
7483     myGroupID = src.myGroupID;
7484   }
7485
7486   static int HashCode(const ComparableElement& se, int limit )
7487   {
7488     return ::HashCode( se.mySumID, limit );
7489   }
7490   static Standard_Boolean IsEqual(const ComparableElement& se1, const ComparableElement& se2 )
7491   {
7492     return ( se1 == se2 );
7493   }
7494
7495 };
7496
7497 //=======================================================================
7498 //function : FindEqualElements
7499 //purpose  : Return list of group of elements built on the same nodes.
7500 //           Search among theElements or in the whole mesh if theElements is empty
7501 //=======================================================================
7502
7503 void SMESH_MeshEditor::FindEqualElements( TIDSortedElemSet &        theElements,
7504                                           TListOfListOfElementsID & theGroupsOfElementsID )
7505 {
7506   ClearLastCreated();
7507
7508   SMDS_ElemIteratorPtr elemIt;
7509   if ( theElements.empty() ) elemIt = GetMeshDS()->elementsIterator();
7510   else                       elemIt = SMESHUtils::elemSetIterator( theElements );
7511
7512   typedef NCollection_Map< ComparableElement, ComparableElement > TMapOfElements;
7513   typedef std::list<int>                                          TGroupOfElems;
7514   TMapOfElements               mapOfElements;
7515   std::vector< TGroupOfElems > arrayOfGroups;
7516   TGroupOfElems                groupOfElems;
7517
7518   while ( elemIt->more() )
7519   {
7520     const SMDS_MeshElement* curElem = elemIt->next();
7521     if ( curElem->IsNull() )
7522       continue;
7523     ComparableElement      compElem = curElem;
7524     // check uniqueness
7525     const ComparableElement& elemInSet = mapOfElements.Added( compElem );
7526     if ( elemInSet.GetElem() != curElem ) // coincident elem
7527     {
7528       int& iG = elemInSet.GroupID();
7529       if ( iG < 0 )
7530       {
7531         iG = arrayOfGroups.size();
7532         arrayOfGroups.push_back( groupOfElems );
7533         arrayOfGroups[ iG ].push_back( elemInSet.GetElem()->GetID() );
7534       }
7535       arrayOfGroups[ iG ].push_back( curElem->GetID() );
7536     }
7537   }
7538
7539   groupOfElems.clear();
7540   std::vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
7541   for ( ; groupIt != arrayOfGroups.end(); ++groupIt )
7542   {
7543     if ( groupIt->size() > 1 ) {
7544       //groupOfElems.sort(); -- theElements are sorted already
7545       theGroupsOfElementsID.emplace_back( *groupIt );
7546     }
7547   }
7548 }
7549
7550 //=======================================================================
7551 //function : MergeElements
7552 //purpose  : In each given group, substitute all elements by the first one.
7553 //=======================================================================
7554
7555 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
7556 {
7557   ClearLastCreated();
7558
7559   typedef list<int> TListOfIDs;
7560   TListOfIDs rmElemIds; // IDs of elems to remove
7561
7562   SMESHDS_Mesh* aMesh = GetMeshDS();
7563
7564   TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
7565   while ( groupsIt != theGroupsOfElementsID.end() ) {
7566     TListOfIDs& aGroupOfElemID = *groupsIt;
7567     aGroupOfElemID.sort();
7568     int elemIDToKeep = aGroupOfElemID.front();
7569     const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
7570     aGroupOfElemID.pop_front();
7571     TListOfIDs::iterator idIt = aGroupOfElemID.begin();
7572     while ( idIt != aGroupOfElemID.end() ) {
7573       int elemIDToRemove = *idIt;
7574       const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
7575       // add the kept element in groups of removed one (PAL15188)
7576       AddToSameGroups( elemToKeep, elemToRemove, aMesh );
7577       rmElemIds.push_back( elemIDToRemove );
7578       ++idIt;
7579     }
7580     ++groupsIt;
7581   }
7582
7583   Remove( rmElemIds, false );
7584 }
7585
7586 //=======================================================================
7587 //function : MergeEqualElements
7588 //purpose  : Remove all but one of elements built on the same nodes.
7589 //=======================================================================
7590
7591 void SMESH_MeshEditor::MergeEqualElements()
7592 {
7593   TIDSortedElemSet aMeshElements; /* empty input ==
7594                                      to merge equal elements in the whole mesh */
7595   TListOfListOfElementsID aGroupsOfElementsID;
7596   FindEqualElements( aMeshElements, aGroupsOfElementsID );
7597   MergeElements( aGroupsOfElementsID );
7598 }
7599
7600 //=======================================================================
7601 //function : findAdjacentFace
7602 //purpose  :
7603 //=======================================================================
7604
7605 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
7606                                                 const SMDS_MeshNode* n2,
7607                                                 const SMDS_MeshElement* elem)
7608 {
7609   TIDSortedElemSet elemSet, avoidSet;
7610   if ( elem )
7611     avoidSet.insert ( elem );
7612   return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
7613 }
7614
7615 //=======================================================================
7616 //function : findSegment
7617 //purpose  : Return a mesh segment by two nodes one of which can be medium
7618 //=======================================================================
7619
7620 static const SMDS_MeshElement* findSegment(const SMDS_MeshNode* n1,
7621                                            const SMDS_MeshNode* n2)
7622 {
7623   SMDS_ElemIteratorPtr it = n1->GetInverseElementIterator( SMDSAbs_Edge );
7624   while ( it->more() )
7625   {
7626     const SMDS_MeshElement* seg = it->next();
7627     if ( seg->GetNodeIndex( n2 ) >= 0 )
7628       return seg;
7629   }
7630   return 0;
7631 }
7632
7633 //=======================================================================
7634 //function : FindFreeBorder
7635 //purpose  :
7636 //=======================================================================
7637
7638 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
7639
7640 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode*             theFirstNode,
7641                                        const SMDS_MeshNode*             theSecondNode,
7642                                        const SMDS_MeshNode*             theLastNode,
7643                                        list< const SMDS_MeshNode* > &   theNodes,
7644                                        list< const SMDS_MeshElement* >& theFaces)
7645 {
7646   if ( !theFirstNode || !theSecondNode )
7647     return false;
7648   // find border face between theFirstNode and theSecondNode
7649   const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
7650   if ( !curElem )
7651     return false;
7652
7653   theFaces.push_back( curElem );
7654   theNodes.push_back( theFirstNode );
7655   theNodes.push_back( theSecondNode );
7656
7657   const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
7658   //TIDSortedElemSet foundElems;
7659   bool needTheLast = ( theLastNode != 0 );
7660
7661   vector<const SMDS_MeshNode*> nodes;
7662   
7663   while ( nStart != theLastNode ) {
7664     if ( nStart == theFirstNode )
7665       return !needTheLast;
7666
7667     // find all free border faces sharing nStart
7668
7669     list< const SMDS_MeshElement* > curElemList;
7670     list< const SMDS_MeshNode* >    nStartList;
7671     SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
7672     while ( invElemIt->more() ) {
7673       const SMDS_MeshElement* e = invElemIt->next();
7674       //if ( e == curElem || foundElems.insert( e ).second ) // e can encounter twice in border
7675       {
7676         // get nodes
7677         nodes.assign( SMDS_MeshElement::iterator( e->interlacedNodesIterator() ),
7678                       SMDS_MeshElement::iterator() );
7679         nodes.push_back( nodes[ 0 ]);
7680
7681         // check 2 links
7682         int iNode = 0, nbNodes = nodes.size() - 1;
7683         for ( iNode = 0; iNode < nbNodes; iNode++ )
7684           if ((( nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
7685                ( nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
7686               ( ControlFreeBorder( &nodes[ iNode ], e->GetID() )))
7687           {
7688             nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart )]);
7689             curElemList.push_back( e );
7690           }
7691       }
7692     }
7693     // analyse the found
7694
7695     int nbNewBorders = curElemList.size();
7696     if ( nbNewBorders == 0 ) {
7697       // no free border furthermore
7698       return !needTheLast;
7699     }
7700     else if ( nbNewBorders == 1 ) {
7701       // one more element found
7702       nIgnore = nStart;
7703       nStart = nStartList.front();
7704       curElem = curElemList.front();
7705       theFaces.push_back( curElem );
7706       theNodes.push_back( nStart );
7707     }
7708     else {
7709       // several continuations found
7710       list< const SMDS_MeshElement* >::iterator curElemIt;
7711       list< const SMDS_MeshNode* >::iterator nStartIt;
7712       // check if one of them reached the last node
7713       if ( needTheLast ) {
7714         for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7715              curElemIt!= curElemList.end();
7716              curElemIt++, nStartIt++ )
7717           if ( *nStartIt == theLastNode ) {
7718             theFaces.push_back( *curElemIt );
7719             theNodes.push_back( *nStartIt );
7720             return true;
7721           }
7722       }
7723       // find the best free border by the continuations
7724       list<const SMDS_MeshNode*>    contNodes[ 2 ], *cNL;
7725       list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
7726       for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7727            curElemIt!= curElemList.end();
7728            curElemIt++, nStartIt++ )
7729       {
7730         cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
7731         cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
7732         // find one more free border
7733         if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
7734           cNL->clear();
7735           cFL->clear();
7736         }
7737         else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
7738           // choice: clear a worse one
7739           int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
7740           int   iWorse = ( needTheLast ? 1 - iLongest : iLongest );
7741           contNodes[ iWorse ].clear();
7742           contFaces[ iWorse ].clear();
7743         }
7744       }
7745       if ( contNodes[0].empty() && contNodes[1].empty() )
7746         return false;
7747
7748       // push_back the best free border
7749       cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
7750       cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
7751       //theNodes.pop_back(); // remove nIgnore
7752       theNodes.pop_back(); // remove nStart
7753       //theFaces.pop_back(); // remove curElem
7754       theNodes.splice( theNodes.end(), *cNL );
7755       theFaces.splice( theFaces.end(), *cFL );
7756       return true;
7757
7758     } // several continuations found
7759   } // while ( nStart != theLastNode )
7760
7761   return true;
7762 }
7763
7764 //=======================================================================
7765 //function : CheckFreeBorderNodes
7766 //purpose  : Return true if the tree nodes are on a free border
7767 //=======================================================================
7768
7769 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
7770                                             const SMDS_MeshNode* theNode2,
7771                                             const SMDS_MeshNode* theNode3)
7772 {
7773   list< const SMDS_MeshNode* > nodes;
7774   list< const SMDS_MeshElement* > faces;
7775   return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
7776 }
7777
7778 //=======================================================================
7779 //function : SewFreeBorder
7780 //purpose  :
7781 //warning  : for border-to-side sewing theSideSecondNode is considered as
7782 //           the last side node and theSideThirdNode is not used
7783 //=======================================================================
7784
7785 SMESH_MeshEditor::Sew_Error
7786 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
7787                                  const SMDS_MeshNode* theBordSecondNode,
7788                                  const SMDS_MeshNode* theBordLastNode,
7789                                  const SMDS_MeshNode* theSideFirstNode,
7790                                  const SMDS_MeshNode* theSideSecondNode,
7791                                  const SMDS_MeshNode* theSideThirdNode,
7792                                  const bool           theSideIsFreeBorder,
7793                                  const bool           toCreatePolygons,
7794                                  const bool           toCreatePolyedrs)
7795 {
7796   ClearLastCreated();
7797
7798   Sew_Error aResult = SEW_OK;
7799
7800   // ====================================
7801   //    find side nodes and elements
7802   // ====================================
7803
7804   list< const SMDS_MeshNode* >    nSide[ 2 ];
7805   list< const SMDS_MeshElement* > eSide[ 2 ];
7806   list< const SMDS_MeshNode* >::iterator    nIt[ 2 ];
7807   list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
7808
7809   // Free border 1
7810   // --------------
7811   if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
7812                       nSide[0], eSide[0])) {
7813     MESSAGE(" Free Border 1 not found " );
7814     aResult = SEW_BORDER1_NOT_FOUND;
7815   }
7816   if (theSideIsFreeBorder) {
7817     // Free border 2
7818     // --------------
7819     if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
7820                         nSide[1], eSide[1])) {
7821       MESSAGE(" Free Border 2 not found " );
7822       aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
7823     }
7824   }
7825   if ( aResult != SEW_OK )
7826     return aResult;
7827
7828   if (!theSideIsFreeBorder) {
7829     // Side 2
7830     // --------------
7831
7832     // -------------------------------------------------------------------------
7833     // Algo:
7834     // 1. If nodes to merge are not coincident, move nodes of the free border
7835     //    from the coord sys defined by the direction from the first to last
7836     //    nodes of the border to the correspondent sys of the side 2
7837     // 2. On the side 2, find the links most co-directed with the correspondent
7838     //    links of the free border
7839     // -------------------------------------------------------------------------
7840
7841     // 1. Since sewing may break if there are volumes to split on the side 2,
7842     //    we won't move nodes but just compute new coordinates for them
7843     typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
7844     TNodeXYZMap nBordXYZ;
7845     list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
7846     list< const SMDS_MeshNode* >::iterator nBordIt;
7847
7848     gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
7849     gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
7850     gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
7851     gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
7852     double tol2 = 1.e-8;
7853     gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
7854     if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
7855       // Need node movement.
7856
7857       // find X and Z axes to create trsf
7858       gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
7859       gp_Vec X = Zs ^ Zb;
7860       if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
7861         // Zb || Zs
7862         X = gp_Ax2( gp::Origin(), Zb ).XDirection();
7863
7864       // coord systems
7865       gp_Ax3 toBordAx( Pb1, Zb, X );
7866       gp_Ax3 fromSideAx( Ps1, Zs, X );
7867       gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
7868       // set trsf
7869       gp_Trsf toBordSys, fromSide2Sys;
7870       toBordSys.SetTransformation( toBordAx );
7871       fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
7872       fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
7873
7874       // move
7875       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
7876         const SMDS_MeshNode* n = *nBordIt;
7877         gp_XYZ xyz( n->X(),n->Y(),n->Z() );
7878         toBordSys.Transforms( xyz );
7879         fromSide2Sys.Transforms( xyz );
7880         nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
7881       }
7882     }
7883     else {
7884       // just insert nodes XYZ in the nBordXYZ map
7885       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
7886         const SMDS_MeshNode* n = *nBordIt;
7887         nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
7888       }
7889     }
7890
7891     // 2. On the side 2, find the links most co-directed with the correspondent
7892     //    links of the free border
7893
7894     list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
7895     list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
7896     sideNodes.push_back( theSideFirstNode );
7897
7898     bool hasVolumes = false;
7899     LinkID_Gen aLinkID_Gen( GetMeshDS() );
7900     set<long> foundSideLinkIDs, checkedLinkIDs;
7901     SMDS_VolumeTool volume;
7902     //const SMDS_MeshNode* faceNodes[ 4 ];
7903
7904     const SMDS_MeshNode*    sideNode;
7905     const SMDS_MeshElement* sideElem  = 0;
7906     const SMDS_MeshNode* prevSideNode = theSideFirstNode;
7907     const SMDS_MeshNode* prevBordNode = theBordFirstNode;
7908     nBordIt = bordNodes.begin();
7909     nBordIt++;
7910     // border node position and border link direction to compare with
7911     gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
7912     gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
7913     // choose next side node by link direction or by closeness to
7914     // the current border node:
7915     bool searchByDir = ( *nBordIt != theBordLastNode );
7916     do {
7917       // find the next node on the Side 2
7918       sideNode = 0;
7919       double maxDot = -DBL_MAX, minDist = DBL_MAX;
7920       long linkID;
7921       checkedLinkIDs.clear();
7922       gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
7923
7924       // loop on inverse elements of current node (prevSideNode) on the Side 2
7925       SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
7926       while ( invElemIt->more() )
7927       {
7928         const SMDS_MeshElement* elem = invElemIt->next();
7929         // prepare data for a loop on links coming to prevSideNode, of a face or a volume
7930         int iPrevNode = 0, iNode = 0, nbNodes = elem->NbNodes();
7931         vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
7932         bool isVolume = volume.Set( elem );
7933         const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
7934         if ( isVolume ) // --volume
7935           hasVolumes = true;
7936         else if ( elem->GetType() == SMDSAbs_Face ) { // --face
7937           // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
7938           SMDS_NodeIteratorPtr nIt = elem->interlacedNodesIterator();
7939           while ( nIt->more() ) {
7940             nodes[ iNode ] = cast2Node( nIt->next() );
7941             if ( nodes[ iNode++ ] == prevSideNode )
7942               iPrevNode = iNode - 1;
7943           }
7944           // there are 2 links to check
7945           nbNodes = 2;
7946         }
7947         else // --edge
7948           continue;
7949         // loop on links, to be precise, on the second node of links
7950         for ( iNode = 0; iNode < nbNodes; iNode++ ) {
7951           const SMDS_MeshNode* n = nodes[ iNode ];
7952           if ( isVolume ) {
7953             if ( !volume.IsLinked( n, prevSideNode ))
7954               continue;
7955           }
7956           else {
7957             if ( iNode ) // a node before prevSideNode
7958               n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
7959             else         // a node after prevSideNode
7960               n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
7961           }
7962           // check if this link was already used
7963           long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
7964           bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
7965           if (!isJustChecked &&
7966               foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
7967           {
7968             // test a link geometrically
7969             gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
7970             bool linkIsBetter = false;
7971             double dot = 0.0, dist = 0.0;
7972             if ( searchByDir ) { // choose most co-directed link
7973               dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
7974               linkIsBetter = ( dot > maxDot );
7975             }
7976             else { // choose link with the node closest to bordPos
7977               dist = ( nextXYZ - bordPos ).SquareModulus();
7978               linkIsBetter = ( dist < minDist );
7979             }
7980             if ( linkIsBetter ) {
7981               maxDot = dot;
7982               minDist = dist;
7983               linkID = iLink;
7984               sideNode = n;
7985               sideElem = elem;
7986             }
7987           }
7988         }
7989       } // loop on inverse elements of prevSideNode
7990
7991       if ( !sideNode ) {
7992         MESSAGE(" Can't find path by links of the Side 2 ");
7993         return SEW_BAD_SIDE_NODES;
7994       }
7995       sideNodes.push_back( sideNode );
7996       sideElems.push_back( sideElem );
7997       foundSideLinkIDs.insert ( linkID );
7998       prevSideNode = sideNode;
7999
8000       if ( *nBordIt == theBordLastNode )
8001         searchByDir = false;
8002       else {
8003         // find the next border link to compare with
8004         gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8005         searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8006         // move to next border node if sideNode is before forward border node (bordPos)
8007         while ( *nBordIt != theBordLastNode && !searchByDir ) {
8008           prevBordNode = *nBordIt;
8009           nBordIt++;
8010           bordPos = nBordXYZ[ *nBordIt ];
8011           bordDir = bordPos - nBordXYZ[ prevBordNode ];
8012           searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8013         }
8014       }
8015     }
8016     while ( sideNode != theSideSecondNode );
8017
8018     if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8019       MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8020       return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8021     }
8022   } // end nodes search on the side 2
8023
8024   // ============================
8025   // sew the border to the side 2
8026   // ============================
8027
8028   int nbNodes[]  = { (int)nSide[0].size(), (int)nSide[1].size() };
8029   int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8030
8031   bool toMergeConformal = ( nbNodes[0] == nbNodes[1] );
8032   if ( toMergeConformal && toCreatePolygons )
8033   {
8034     // do not merge quadrangles if polygons are OK (IPAL0052824)
8035     eIt[0] = eSide[0].begin();
8036     eIt[1] = eSide[1].begin();
8037     bool allQuads[2] = { true, true };
8038     for ( int iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8039       for ( ; allQuads[iBord] && eIt[iBord] != eSide[iBord].end(); ++eIt[iBord] )
8040         allQuads[iBord] = ( (*eIt[iBord])->NbCornerNodes() == 4 );
8041     }
8042     toMergeConformal = ( !allQuads[0] && !allQuads[1] );
8043   }
8044
8045   TListOfListOfNodes nodeGroupsToMerge;
8046   if (( toMergeConformal ) ||
8047       ( theSideIsFreeBorder && !theSideThirdNode )) {
8048
8049     // all nodes are to be merged
8050
8051     for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8052          nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8053          nIt[0]++, nIt[1]++ )
8054     {
8055       nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8056       nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8057       nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8058     }
8059   }
8060   else {
8061
8062     // insert new nodes into the border and the side to get equal nb of segments
8063
8064     // get normalized parameters of nodes on the borders
8065     vector< double > param[ 2 ];
8066     param[0].resize( maxNbNodes );
8067     param[1].resize( maxNbNodes );
8068     int iNode, iBord;
8069     for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8070       list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8071       list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8072       const SMDS_MeshNode* nPrev = *nIt;
8073       double bordLength = 0;
8074       for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8075         const SMDS_MeshNode* nCur = *nIt;
8076         gp_XYZ segment (nCur->X() - nPrev->X(),
8077                         nCur->Y() - nPrev->Y(),
8078                         nCur->Z() - nPrev->Z());
8079         double segmentLen = segment.Modulus();
8080         bordLength += segmentLen;
8081         param[ iBord ][ iNode ] = bordLength;
8082         nPrev = nCur;
8083       }
8084       // normalize within [0,1]
8085       for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8086         param[ iBord ][ iNode ] /= bordLength;
8087       }
8088     }
8089
8090     // loop on border segments
8091     const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8092     int i[ 2 ] = { 0, 0 };
8093     nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8094     nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8095
8096     // element can be split while iterating on border if it has two edges in the border
8097     std::map< const SMDS_MeshElement* , const SMDS_MeshElement* > elemReplaceMap;
8098     std::map< const SMDS_MeshElement* , const SMDS_MeshElement* >::iterator elemReplaceMapIt;
8099
8100     TElemOfNodeListMap insertMap;
8101     TElemOfNodeListMap::iterator insertMapIt;
8102     // insertMap is
8103     // key:   elem to insert nodes into
8104     // value: 2 nodes to insert between + nodes to be inserted
8105     do {
8106       bool next[ 2 ] = { false, false };
8107
8108       // find min adjacent segment length after sewing
8109       double nextParam = 10., prevParam = 0;
8110       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8111         if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8112           nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8113         if ( i[ iBord ] > 0 )
8114           prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8115       }
8116       double  minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8117       double  maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8118       double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8119
8120       // choose to insert or to merge nodes
8121       double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8122       if ( Abs( du ) <= minSegLen * 0.2 ) {
8123         // merge
8124         // ------
8125         nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8126         const SMDS_MeshNode* n0 = *nIt[0];
8127         const SMDS_MeshNode* n1 = *nIt[1];
8128         nodeGroupsToMerge.back().push_back( n1 );
8129         nodeGroupsToMerge.back().push_back( n0 );
8130         // position of node of the border changes due to merge
8131         param[ 0 ][ i[0] ] += du;
8132         // move n1 for the sake of elem shape evaluation during insertion.
8133         // n1 will be removed by MergeNodes() anyway
8134         const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8135         next[0] = next[1] = true;
8136       }
8137       else {
8138         // insert
8139         // ------
8140         int intoBord = ( du < 0 ) ? 0 : 1;
8141         const SMDS_MeshElement* elem = *eIt [ intoBord ];
8142         const SMDS_MeshNode*    n1   = nPrev[ intoBord ];
8143         const SMDS_MeshNode*    n2   = *nIt [ intoBord ];
8144         const SMDS_MeshNode*    nIns = *nIt [ 1 - intoBord ];
8145         if ( intoBord == 1 ) {
8146           // move node of the border to be on a link of elem of the side
8147           SMESH_NodeXYZ p1( n1 ), p2( n2 );
8148           double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8149           gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8150           GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8151         }
8152         elemReplaceMapIt = elemReplaceMap.find( elem );
8153         if ( elemReplaceMapIt != elemReplaceMap.end() )
8154           elem = elemReplaceMapIt->second;
8155
8156         insertMapIt = insertMap.find( elem );
8157         bool  notFound = ( insertMapIt == insertMap.end() );
8158         bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8159         if ( otherLink ) {
8160           // insert into another link of the same element:
8161           // 1. perform insertion into the other link of the elem
8162           list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8163           const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8164           const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8165           InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8166           // 2. perform insertion into the link of adjacent faces
8167           while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem )) {
8168             InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8169           }
8170           while ( const SMDS_MeshElement* seg = findSegment( n12, n22 )) {
8171             InsertNodesIntoLink( seg, n12, n22, nodeList );
8172           }
8173           if (toCreatePolyedrs) {
8174             // perform insertion into the links of adjacent volumes
8175             UpdateVolumes(n12, n22, nodeList);
8176           }
8177           // 3. find an element appeared on n1 and n2 after the insertion
8178           insertMap.erase( insertMapIt );
8179           const SMDS_MeshElement* elem2 = findAdjacentFace( n1, n2, 0 );
8180           elemReplaceMap.insert( std::make_pair( elem, elem2 ));
8181           elem = elem2;
8182         }
8183         if ( notFound || otherLink ) {
8184           // add element and nodes of the side into the insertMap
8185           insertMapIt = insertMap.insert( make_pair( elem, list<const SMDS_MeshNode*>() )).first;
8186           (*insertMapIt).second.push_back( n1 );
8187           (*insertMapIt).second.push_back( n2 );
8188         }
8189         // add node to be inserted into elem
8190         (*insertMapIt).second.push_back( nIns );
8191         next[ 1 - intoBord ] = true;
8192       }
8193
8194       // go to the next segment
8195       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8196         if ( next[ iBord ] ) {
8197           if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8198             eIt[ iBord ]++;
8199           nPrev[ iBord ] = *nIt[ iBord ];
8200           nIt[ iBord ]++; i[ iBord ]++;
8201         }
8202       }
8203     }
8204     while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8205
8206     // perform insertion of nodes into elements
8207
8208     for (insertMapIt = insertMap.begin();
8209          insertMapIt != insertMap.end();
8210          insertMapIt++ )
8211     {
8212       const SMDS_MeshElement* elem = (*insertMapIt).first;
8213       list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8214       if ( nodeList.size() < 3 ) continue;
8215       const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8216       const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8217
8218       InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8219
8220       while ( const SMDS_MeshElement* seg = findSegment( n1, n2 )) {
8221         InsertNodesIntoLink( seg, n1, n2, nodeList );
8222       }
8223
8224       if ( !theSideIsFreeBorder ) {
8225         // look for and insert nodes into the faces adjacent to elem
8226         while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem )) {
8227           InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8228         }
8229       }
8230       if (toCreatePolyedrs) {
8231         // perform insertion into the links of adjacent volumes
8232         UpdateVolumes(n1, n2, nodeList);
8233       }
8234     }
8235   } // end: insert new nodes
8236
8237   MergeNodes ( nodeGroupsToMerge );
8238
8239
8240   // Remove coincident segments
8241
8242   // get new segments
8243   TIDSortedElemSet segments;
8244   SMESH_SequenceOfElemPtr newFaces;
8245   for ( size_t i = 0; i < myLastCreatedElems.size(); ++i )
8246   {
8247     if ( !myLastCreatedElems[i] ) continue;
8248     if ( myLastCreatedElems[i]->GetType() == SMDSAbs_Edge )
8249       segments.insert( segments.end(), myLastCreatedElems[i] );
8250     else
8251       newFaces.push_back( myLastCreatedElems[i] );
8252   }
8253   // get segments adjacent to merged nodes
8254   TListOfListOfNodes::iterator groupIt = nodeGroupsToMerge.begin();
8255   for ( ; groupIt != nodeGroupsToMerge.end(); groupIt++ )
8256   {
8257     const list<const SMDS_MeshNode*>& nodes = *groupIt;
8258     if ( nodes.front()->IsNull() ) continue;
8259     SMDS_ElemIteratorPtr segIt = nodes.front()->GetInverseElementIterator( SMDSAbs_Edge );
8260     while ( segIt->more() )
8261       segments.insert( segIt->next() );
8262   }
8263
8264   // find coincident
8265   TListOfListOfElementsID equalGroups;
8266   if ( !segments.empty() )
8267     FindEqualElements( segments, equalGroups );
8268   if ( !equalGroups.empty() )
8269   {
8270     // remove from segments those that will be removed
8271     TListOfListOfElementsID::iterator itGroups = equalGroups.begin();
8272     for ( ; itGroups != equalGroups.end(); ++itGroups )
8273     {
8274       list< int >& group = *itGroups;
8275       list< int >::iterator id = group.begin();
8276       for ( ++id; id != group.end(); ++id )
8277         if ( const SMDS_MeshElement* seg = GetMeshDS()->FindElement( *id ))
8278           segments.erase( seg );
8279     }
8280     // remove equal segments
8281     MergeElements( equalGroups );
8282
8283     // restore myLastCreatedElems
8284     myLastCreatedElems = newFaces;
8285     TIDSortedElemSet::iterator seg = segments.begin();
8286     for ( ; seg != segments.end(); ++seg )
8287       myLastCreatedElems.push_back( *seg );
8288   }
8289
8290   return aResult;
8291 }
8292
8293 //=======================================================================
8294 //function : InsertNodesIntoLink
8295 //purpose  : insert theNodesToInsert into theElement between theBetweenNode1
8296 //           and theBetweenNode2 and split theElement
8297 //=======================================================================
8298
8299 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement*     theElement,
8300                                            const SMDS_MeshNode*        theBetweenNode1,
8301                                            const SMDS_MeshNode*        theBetweenNode2,
8302                                            list<const SMDS_MeshNode*>& theNodesToInsert,
8303                                            const bool                  toCreatePoly)
8304 {
8305   if ( !theElement ) return;
8306
8307   SMESHDS_Mesh *aMesh = GetMeshDS();
8308   vector<const SMDS_MeshElement*> newElems;
8309
8310   if ( theElement->GetType() == SMDSAbs_Edge )
8311   {
8312     theNodesToInsert.push_front( theBetweenNode1 );
8313     theNodesToInsert.push_back ( theBetweenNode2 );
8314     list<const SMDS_MeshNode*>::iterator n = theNodesToInsert.begin();
8315     const SMDS_MeshNode* n1 = *n;
8316     for ( ++n; n != theNodesToInsert.end(); ++n )
8317     {
8318       const SMDS_MeshNode* n2 = *n;
8319       if ( const SMDS_MeshElement* seg = aMesh->FindEdge( n1, n2 ))
8320         AddToSameGroups( seg, theElement, aMesh );
8321       else
8322         newElems.push_back( aMesh->AddEdge ( n1, n2 ));
8323       n1 = n2;
8324     }
8325     theNodesToInsert.pop_front();
8326     theNodesToInsert.pop_back();
8327
8328     if ( theElement->IsQuadratic() ) // add a not split part
8329     {
8330       vector<const SMDS_MeshNode*> nodes( theElement->begin_nodes(),
8331                                           theElement->end_nodes() );
8332       int iOther = 0, nbN = nodes.size();
8333       for ( ; iOther < nbN; ++iOther )
8334         if ( nodes[iOther] != theBetweenNode1 &&
8335              nodes[iOther] != theBetweenNode2 )
8336           break;
8337       if      ( iOther == 0 )
8338       {
8339         if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[0], nodes[1] ))
8340           AddToSameGroups( seg, theElement, aMesh );
8341         else
8342           newElems.push_back( aMesh->AddEdge ( nodes[0], nodes[1] ));
8343       }
8344       else if ( iOther == 2 )
8345       {
8346         if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[1], nodes[2] ))
8347           AddToSameGroups( seg, theElement, aMesh );
8348         else
8349           newElems.push_back( aMesh->AddEdge ( nodes[1], nodes[2] ));
8350       }
8351     }
8352     // treat new elements
8353     for ( size_t i = 0; i < newElems.size(); ++i )
8354       if ( newElems[i] )
8355       {
8356         aMesh->SetMeshElementOnShape( newElems[i], theElement->getshapeId() );
8357         myLastCreatedElems.push_back( newElems[i] );
8358       }
8359     ReplaceElemInGroups( theElement, newElems, aMesh );
8360     aMesh->RemoveElement( theElement );
8361     return;
8362
8363   } // if ( theElement->GetType() == SMDSAbs_Edge )
8364
8365   const SMDS_MeshElement* theFace = theElement;
8366   if ( theFace->GetType() != SMDSAbs_Face ) return;
8367
8368   // find indices of 2 link nodes and of the rest nodes
8369   int iNode = 0, il1, il2, i3, i4;
8370   il1 = il2 = i3 = i4 = -1;
8371   vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8372
8373   SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8374   while ( nodeIt->more() ) {
8375     const SMDS_MeshNode* n = nodeIt->next();
8376     if ( n == theBetweenNode1 )
8377       il1 = iNode;
8378     else if ( n == theBetweenNode2 )
8379       il2 = iNode;
8380     else if ( i3 < 0 )
8381       i3 = iNode;
8382     else
8383       i4 = iNode;
8384     nodes[ iNode++ ] = n;
8385   }
8386   if ( il1 < 0 || il2 < 0 || i3 < 0 )
8387     return ;
8388
8389   // arrange link nodes to go one after another regarding the face orientation
8390   bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8391   list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8392   if ( reverse ) {
8393     iNode = il1;
8394     il1 = il2;
8395     il2 = iNode;
8396     aNodesToInsert.reverse();
8397   }
8398   // check that not link nodes of a quadrangles are in good order
8399   int nbFaceNodes = theFace->NbNodes();
8400   if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8401     iNode = i3;
8402     i3 = i4;
8403     i4 = iNode;
8404   }
8405
8406   if (toCreatePoly || theFace->IsPoly()) {
8407
8408     iNode = 0;
8409     vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8410
8411     // add nodes of face up to first node of link
8412     bool isFLN = false;
8413     SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8414     while ( nodeIt->more() && !isFLN ) {
8415       const SMDS_MeshNode* n = nodeIt->next();
8416       poly_nodes[iNode++] = n;
8417       isFLN = ( n == nodes[il1] );
8418     }
8419     // add nodes to insert
8420     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8421     for (; nIt != aNodesToInsert.end(); nIt++) {
8422       poly_nodes[iNode++] = *nIt;
8423     }
8424     // add nodes of face starting from last node of link
8425     while ( nodeIt->more() ) {
8426       const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8427       poly_nodes[iNode++] = n;
8428     }
8429
8430     // make a new face
8431     newElems.push_back( aMesh->AddPolygonalFace( poly_nodes ));
8432   }
8433
8434   else if ( !theFace->IsQuadratic() )
8435   {
8436     // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8437     int nbLinkNodes = 2 + aNodesToInsert.size();
8438     //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8439     vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8440     linkNodes[ 0 ] = nodes[ il1 ];
8441     linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8442     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8443     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8444       linkNodes[ iNode++ ] = *nIt;
8445     }
8446     // decide how to split a quadrangle: compare possible variants
8447     // and choose which of splits to be a quadrangle
8448     int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad = 0;
8449     if ( nbFaceNodes == 3 ) {
8450       iBestQuad = nbSplits;
8451       i4 = i3;
8452     }
8453     else if ( nbFaceNodes == 4 ) {
8454       SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8455       double aBestRate = DBL_MAX;
8456       for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8457         i1 = 0; i2 = 1;
8458         double aBadRate = 0;
8459         // evaluate elements quality
8460         for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8461           if ( iSplit == iQuad ) {
8462             SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8463                                    linkNodes[ i2++ ],
8464                                    nodes[ i3 ],
8465                                    nodes[ i4 ]);
8466             aBadRate += getBadRate( &quad, aCrit );
8467           }
8468           else {
8469             SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8470                                    linkNodes[ i2++ ],
8471                                    nodes[ iSplit < iQuad ? i4 : i3 ]);
8472             aBadRate += getBadRate( &tria, aCrit );
8473           }
8474         }
8475         // choice
8476         if ( aBadRate < aBestRate ) {
8477           iBestQuad = iQuad;
8478           aBestRate = aBadRate;
8479         }
8480       }
8481     }
8482
8483     // create new elements
8484     i1 = 0; i2 = 1;
8485     for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ )
8486     {
8487       if ( iSplit == iBestQuad )
8488         newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8489                                             linkNodes[ i2++ ],
8490                                             nodes[ i3 ],
8491                                             nodes[ i4 ]));
8492       else
8493         newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8494                                             linkNodes[ i2++ ],
8495                                             nodes[ iSplit < iBestQuad ? i4 : i3 ]));
8496     }
8497
8498     const SMDS_MeshNode* newNodes[ 4 ];
8499     newNodes[ 0 ] = linkNodes[ i1 ];
8500     newNodes[ 1 ] = linkNodes[ i2 ];
8501     newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
8502     newNodes[ 3 ] = nodes[ i4 ];
8503     if (iSplit == iBestQuad)
8504       newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] ));
8505     else
8506       newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] ));
8507
8508   } // end if(!theFace->IsQuadratic())
8509
8510   else { // theFace is quadratic
8511     // we have to split theFace on simple triangles and one simple quadrangle
8512     int tmp = il1/2;
8513     int nbshift = tmp*2;
8514     // shift nodes in nodes[] by nbshift
8515     int i,j;
8516     for(i=0; i<nbshift; i++) {
8517       const SMDS_MeshNode* n = nodes[0];
8518       for(j=0; j<nbFaceNodes-1; j++) {
8519         nodes[j] = nodes[j+1];
8520       }
8521       nodes[nbFaceNodes-1] = n;
8522     }
8523     il1 = il1 - nbshift;
8524     // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
8525     //   n0      n1     n2    n0      n1     n2
8526     //     +-----+-----+        +-----+-----+
8527     //      \         /         |           |
8528     //       \       /          |           |
8529     //      n5+     +n3       n7+           +n3
8530     //         \   /            |           |
8531     //          \ /             |           |
8532     //           +              +-----+-----+
8533     //           n4           n6      n5     n4
8534
8535     // create new elements
8536     int n1,n2,n3;
8537     if ( nbFaceNodes == 6 ) { // quadratic triangle
8538       newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8539       if ( theFace->IsMediumNode(nodes[il1]) ) {
8540         // create quadrangle
8541         newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[5] ));
8542         n1 = 1;
8543         n2 = 2;
8544         n3 = 3;
8545       }
8546       else {
8547         // create quadrangle
8548         newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[5] ));
8549         n1 = 0;
8550         n2 = 1;
8551         n3 = 5;
8552       }
8553     }
8554     else { // nbFaceNodes==8 - quadratic quadrangle
8555       newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8556       newElems.push_back( aMesh->AddFace( nodes[5], nodes[6], nodes[7] ));
8557       newElems.push_back( aMesh->AddFace( nodes[5], nodes[7], nodes[3] ));
8558       if ( theFace->IsMediumNode( nodes[ il1 ])) {
8559         // create quadrangle
8560         newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[7] ));
8561         n1 = 1;
8562         n2 = 2;
8563         n3 = 3;
8564       }
8565       else {
8566         // create quadrangle
8567         newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[7] ));
8568         n1 = 0;
8569         n2 = 1;
8570         n3 = 7;
8571       }
8572     }
8573     // create needed triangles using n1,n2,n3 and inserted nodes
8574     int nbn = 2 + aNodesToInsert.size();
8575     vector<const SMDS_MeshNode*> aNodes(nbn);
8576     aNodes[0    ] = nodes[n1];
8577     aNodes[nbn-1] = nodes[n2];
8578     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8579     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8580       aNodes[iNode++] = *nIt;
8581     }
8582     for ( i = 1; i < nbn; i++ )
8583       newElems.push_back( aMesh->AddFace( aNodes[i-1], aNodes[i], nodes[n3] ));
8584   }
8585
8586   // remove the old face
8587   for ( size_t i = 0; i < newElems.size(); ++i )
8588     if ( newElems[i] )
8589     {
8590       aMesh->SetMeshElementOnShape( newElems[i], theFace->getshapeId() );
8591       myLastCreatedElems.push_back( newElems[i] );
8592     }
8593   ReplaceElemInGroups( theFace, newElems, aMesh );
8594   aMesh->RemoveElement(theFace);
8595
8596 } // InsertNodesIntoLink()
8597
8598 //=======================================================================
8599 //function : UpdateVolumes
8600 //purpose  :
8601 //=======================================================================
8602
8603 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode*        theBetweenNode1,
8604                                       const SMDS_MeshNode*        theBetweenNode2,
8605                                       list<const SMDS_MeshNode*>& theNodesToInsert)
8606 {
8607   ClearLastCreated();
8608
8609   SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
8610   while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
8611     const SMDS_MeshElement* elem = invElemIt->next();
8612
8613     // check, if current volume has link theBetweenNode1 - theBetweenNode2
8614     SMDS_VolumeTool aVolume (elem);
8615     if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
8616       continue;
8617
8618     // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
8619     int iface, nbFaces = aVolume.NbFaces();
8620     vector<const SMDS_MeshNode *> poly_nodes;
8621     vector<int> quantities (nbFaces);
8622
8623     for (iface = 0; iface < nbFaces; iface++) {
8624       int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
8625       // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
8626       const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
8627
8628       for (int inode = 0; inode < nbFaceNodes; inode++) {
8629         poly_nodes.push_back(faceNodes[inode]);
8630
8631         if (nbInserted == 0) {
8632           if (faceNodes[inode] == theBetweenNode1) {
8633             if (faceNodes[inode + 1] == theBetweenNode2) {
8634               nbInserted = theNodesToInsert.size();
8635
8636               // add nodes to insert
8637               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
8638               for (; nIt != theNodesToInsert.end(); nIt++) {
8639                 poly_nodes.push_back(*nIt);
8640               }
8641             }
8642           }
8643           else if (faceNodes[inode] == theBetweenNode2) {
8644             if (faceNodes[inode + 1] == theBetweenNode1) {
8645               nbInserted = theNodesToInsert.size();
8646
8647               // add nodes to insert in reversed order
8648               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
8649               nIt--;
8650               for (; nIt != theNodesToInsert.begin(); nIt--) {
8651                 poly_nodes.push_back(*nIt);
8652               }
8653               poly_nodes.push_back(*nIt);
8654             }
8655           }
8656           else {
8657           }
8658         }
8659       }
8660       quantities[iface] = nbFaceNodes + nbInserted;
8661     }
8662
8663     // Replace the volume
8664     SMESHDS_Mesh *aMesh = GetMeshDS();
8665
8666     if ( SMDS_MeshElement* newElem = aMesh->AddPolyhedralVolume( poly_nodes, quantities ))
8667     {
8668       aMesh->SetMeshElementOnShape( newElem, elem->getshapeId() );
8669       myLastCreatedElems.push_back( newElem );
8670       ReplaceElemInGroups( elem, newElem, aMesh );
8671     }
8672     aMesh->RemoveElement( elem );
8673   }
8674 }
8675
8676 namespace
8677 {
8678   //================================================================================
8679   /*!
8680    * \brief Transform any volume into data of SMDSEntity_Polyhedra
8681    */
8682   //================================================================================
8683
8684   void volumeToPolyhedron( const SMDS_MeshElement*         elem,
8685                            vector<const SMDS_MeshNode *> & nodes,
8686                            vector<int> &                   nbNodeInFaces )
8687   {
8688     nodes.clear();
8689     nbNodeInFaces.clear();
8690     SMDS_VolumeTool vTool ( elem );
8691     for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
8692     {
8693       const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
8694       nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
8695       nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
8696     }
8697   }
8698 }
8699
8700 //=======================================================================
8701 /*!
8702  * \brief Convert elements contained in a sub-mesh to quadratic
8703  * \return int - nb of checked elements
8704  */
8705 //=======================================================================
8706
8707 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh *   theSm,
8708                                              SMESH_MesherHelper& theHelper,
8709                                              const bool          theForce3d)
8710 {
8711   //MESSAGE("convertElemToQuadratic");
8712   int nbElem = 0;
8713   if( !theSm ) return nbElem;
8714
8715   vector<int> nbNodeInFaces;
8716   vector<const SMDS_MeshNode *> nodes;
8717   SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
8718   while(ElemItr->more())
8719   {
8720     nbElem++;
8721     const SMDS_MeshElement* elem = ElemItr->next();
8722     if( !elem ) continue;
8723
8724     // analyse a necessity of conversion
8725     const SMDSAbs_ElementType aType = elem->GetType();
8726     if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
8727       continue;
8728     const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
8729     bool hasCentralNodes = false;
8730     if ( elem->IsQuadratic() )
8731     {
8732       bool alreadyOK;
8733       switch ( aGeomType ) {
8734       case SMDSEntity_Quad_Triangle:
8735       case SMDSEntity_Quad_Quadrangle:
8736       case SMDSEntity_Quad_Hexa:
8737       case SMDSEntity_Quad_Penta:
8738         alreadyOK = !theHelper.GetIsBiQuadratic(); break;
8739
8740       case SMDSEntity_BiQuad_Triangle:
8741       case SMDSEntity_BiQuad_Quadrangle:
8742       case SMDSEntity_TriQuad_Hexa:
8743       case SMDSEntity_BiQuad_Penta:
8744         alreadyOK = theHelper.GetIsBiQuadratic();
8745         hasCentralNodes = true;
8746         break;
8747       default:
8748         alreadyOK = true;
8749       }
8750       // take into account already present medium nodes
8751       switch ( aType ) {
8752       case SMDSAbs_Volume:
8753         theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
8754       case SMDSAbs_Face:
8755         theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
8756       case SMDSAbs_Edge:
8757         theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
8758       default:;
8759       }
8760       if ( alreadyOK )
8761         continue;
8762     }
8763     // get elem data needed to re-create it
8764     //
8765     const int id      = elem->GetID();
8766     const int nbNodes = elem->NbCornerNodes();
8767     nodes.assign(elem->begin_nodes(), elem->end_nodes());
8768     if ( aGeomType == SMDSEntity_Polyhedra )
8769       nbNodeInFaces = static_cast<const SMDS_MeshVolume* >( elem )->GetQuantities();
8770     else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
8771       volumeToPolyhedron( elem, nodes, nbNodeInFaces );
8772
8773     // remove a linear element
8774     GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
8775
8776     // remove central nodes of biquadratic elements (biquad->quad conversion)
8777     if ( hasCentralNodes )
8778       for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
8779         if ( nodes[i]->NbInverseElements() == 0 )
8780           GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
8781
8782     const SMDS_MeshElement* NewElem = 0;
8783
8784     switch( aType )
8785     {
8786     case SMDSAbs_Edge :
8787     {
8788       NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
8789       break;
8790     }
8791     case SMDSAbs_Face :
8792     {
8793       switch(nbNodes)
8794       {
8795       case 3:
8796         NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
8797         break;
8798       case 4:
8799         NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8800         break;
8801       default:
8802         NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
8803       }
8804       break;
8805     }
8806     case SMDSAbs_Volume :
8807     {
8808       switch( aGeomType )
8809       {
8810       case SMDSEntity_Tetra:
8811         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8812         break;
8813       case SMDSEntity_Pyramid:
8814         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
8815         break;
8816       case SMDSEntity_Penta:
8817       case SMDSEntity_Quad_Penta:
8818       case SMDSEntity_BiQuad_Penta:
8819         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
8820         break;
8821       case SMDSEntity_Hexa:
8822       case SMDSEntity_Quad_Hexa:
8823       case SMDSEntity_TriQuad_Hexa:
8824         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
8825                                       nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
8826         break;
8827       case SMDSEntity_Hexagonal_Prism:
8828       default:
8829         NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
8830       }
8831       break;
8832     }
8833     default :
8834       continue;
8835     }
8836     ReplaceElemInGroups( elem, NewElem, GetMeshDS());
8837     if( NewElem && NewElem->getshapeId() < 1 )
8838       theSm->AddElement( NewElem );
8839   }
8840   return nbElem;
8841 }
8842 //=======================================================================
8843 //function : ConvertToQuadratic
8844 //purpose  :
8845 //=======================================================================
8846
8847 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
8848 {
8849   //MESSAGE("ConvertToQuadratic "<< theForce3d << " " << theToBiQuad);
8850   SMESHDS_Mesh* meshDS = GetMeshDS();
8851
8852   SMESH_MesherHelper aHelper(*myMesh);
8853
8854   aHelper.SetIsQuadratic( true );
8855   aHelper.SetIsBiQuadratic( theToBiQuad );
8856   aHelper.SetElementsOnShape(true);
8857   aHelper.ToFixNodeParameters( true );
8858
8859   // convert elements assigned to sub-meshes
8860   int nbCheckedElems = 0;
8861   if ( myMesh->HasShapeToMesh() )
8862   {
8863     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
8864     {
8865       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
8866       while ( smIt->more() ) {
8867         SMESH_subMesh* sm = smIt->next();
8868         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
8869           aHelper.SetSubShape( sm->GetSubShape() );
8870           nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
8871         }
8872       }
8873     }
8874   }
8875
8876   // convert elements NOT assigned to sub-meshes
8877   int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
8878   if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
8879   {
8880     aHelper.SetElementsOnShape(false);
8881     SMESHDS_SubMesh *smDS = 0;
8882
8883     // convert edges
8884     SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
8885     while( aEdgeItr->more() )
8886     {
8887       const SMDS_MeshEdge* edge = aEdgeItr->next();
8888       if ( !edge->IsQuadratic() )
8889       {
8890         int                  id = edge->GetID();
8891         const SMDS_MeshNode* n1 = edge->GetNode(0);
8892         const SMDS_MeshNode* n2 = edge->GetNode(1);
8893
8894         meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
8895
8896         const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
8897         ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
8898       }
8899       else
8900       {
8901         aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
8902       }
8903     }
8904
8905     // convert faces
8906     SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
8907     while( aFaceItr->more() )
8908     {
8909       const SMDS_MeshFace* face = aFaceItr->next();
8910       if ( !face ) continue;
8911       
8912       const SMDSAbs_EntityType type = face->GetEntityType();
8913       bool alreadyOK;
8914       switch( type )
8915       {
8916       case SMDSEntity_Quad_Triangle:
8917       case SMDSEntity_Quad_Quadrangle:
8918         alreadyOK = !theToBiQuad;
8919         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
8920         break;
8921       case SMDSEntity_BiQuad_Triangle:
8922       case SMDSEntity_BiQuad_Quadrangle:
8923         alreadyOK = theToBiQuad;
8924         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
8925         break;
8926       default: alreadyOK = false;
8927       }
8928       if ( alreadyOK )
8929         continue;
8930
8931       const int id = face->GetID();
8932       vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
8933
8934       meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
8935
8936       SMDS_MeshFace * NewFace = 0;
8937       switch( type )
8938       {
8939       case SMDSEntity_Triangle:
8940       case SMDSEntity_Quad_Triangle:
8941       case SMDSEntity_BiQuad_Triangle:
8942         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
8943         if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
8944           GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
8945         break;
8946
8947       case SMDSEntity_Quadrangle:
8948       case SMDSEntity_Quad_Quadrangle:
8949       case SMDSEntity_BiQuad_Quadrangle:
8950         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8951         if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
8952           GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
8953         break;
8954
8955       default:;
8956         NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
8957       }
8958       ReplaceElemInGroups( face, NewFace, GetMeshDS());
8959     }
8960
8961     // convert volumes
8962     vector<int> nbNodeInFaces;
8963     SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
8964     while(aVolumeItr->more())
8965     {
8966       const SMDS_MeshVolume* volume = aVolumeItr->next();
8967       if ( !volume ) continue;
8968
8969       const SMDSAbs_EntityType type = volume->GetEntityType();
8970       if ( volume->IsQuadratic() )
8971       {
8972         bool alreadyOK;
8973         switch ( type )
8974         {
8975         case SMDSEntity_Quad_Hexa:    alreadyOK = !theToBiQuad; break;
8976         case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
8977         case SMDSEntity_Quad_Penta:   alreadyOK = !theToBiQuad; break;
8978         case SMDSEntity_BiQuad_Penta: alreadyOK = theToBiQuad; break;
8979         default:                      alreadyOK = true;
8980         }
8981         if ( alreadyOK )
8982         {
8983           aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
8984           continue;
8985         }
8986       }
8987       const int id = volume->GetID();
8988       vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
8989       if ( type == SMDSEntity_Polyhedra )
8990         nbNodeInFaces = static_cast<const SMDS_MeshVolume* >(volume)->GetQuantities();
8991       else if ( type == SMDSEntity_Hexagonal_Prism )
8992         volumeToPolyhedron( volume, nodes, nbNodeInFaces );
8993
8994       meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
8995
8996       SMDS_MeshVolume * NewVolume = 0;
8997       switch ( type )
8998       {
8999       case SMDSEntity_Tetra:
9000         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9001         break;
9002       case SMDSEntity_Hexa:
9003       case SMDSEntity_Quad_Hexa:
9004       case SMDSEntity_TriQuad_Hexa:
9005         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9006                                       nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9007         for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
9008           if ( nodes[i]->NbInverseElements() == 0 )
9009             GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9010         break;
9011       case SMDSEntity_Pyramid:
9012         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9013                                       nodes[3], nodes[4], id, theForce3d);
9014         break;
9015       case SMDSEntity_Penta:
9016       case SMDSEntity_Quad_Penta:
9017       case SMDSEntity_BiQuad_Penta:
9018         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9019                                       nodes[3], nodes[4], nodes[5], id, theForce3d);
9020         for ( size_t i = 15; i < nodes.size(); ++i ) // rm central nodes
9021           if ( nodes[i]->NbInverseElements() == 0 )
9022             GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9023         break;
9024       case SMDSEntity_Hexagonal_Prism:
9025       default:
9026         NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9027       }
9028       ReplaceElemInGroups(volume, NewVolume, meshDS);
9029     }
9030   }
9031
9032   if ( !theForce3d )
9033   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9034     // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9035     // aHelper.FixQuadraticElements(myError);
9036     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9037   }
9038 }
9039
9040 //================================================================================
9041 /*!
9042  * \brief Makes given elements quadratic
9043  *  \param theForce3d - if true, the medium nodes will be placed in the middle of link
9044  *  \param theElements - elements to make quadratic
9045  */
9046 //================================================================================
9047
9048 void SMESH_MeshEditor::ConvertToQuadratic(const bool        theForce3d,
9049                                           TIDSortedElemSet& theElements,
9050                                           const bool        theToBiQuad)
9051 {
9052   if ( theElements.empty() ) return;
9053
9054   // we believe that all theElements are of the same type
9055   const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9056
9057   // get all nodes shared by theElements
9058   TIDSortedNodeSet allNodes;
9059   TIDSortedElemSet::iterator eIt = theElements.begin();
9060   for ( ; eIt != theElements.end(); ++eIt )
9061     allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9062
9063   // complete theElements with elements of lower dim whose all nodes are in allNodes
9064
9065   TIDSortedElemSet quadAdjacentElems    [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9066   TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9067   TIDSortedNodeSet::iterator nIt = allNodes.begin();
9068   for ( ; nIt != allNodes.end(); ++nIt )
9069   {
9070     const SMDS_MeshNode* n = *nIt;
9071     SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9072     while ( invIt->more() )
9073     {
9074       const SMDS_MeshElement*      e = invIt->next();
9075       const SMDSAbs_ElementType type = e->GetType();
9076       if ( e->IsQuadratic() )
9077       {
9078         quadAdjacentElems[ type ].insert( e );
9079
9080         bool alreadyOK;
9081         switch ( e->GetEntityType() ) {
9082         case SMDSEntity_Quad_Triangle:
9083         case SMDSEntity_Quad_Quadrangle:
9084         case SMDSEntity_Quad_Hexa:         alreadyOK = !theToBiQuad; break;
9085         case SMDSEntity_BiQuad_Triangle:
9086         case SMDSEntity_BiQuad_Quadrangle:
9087         case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; break;
9088         default:                           alreadyOK = true;
9089         }
9090         if ( alreadyOK )
9091           continue;
9092       }
9093       if ( type >= elemType )
9094         continue; // same type or more complex linear element
9095
9096       if ( !checkedAdjacentElems[ type ].insert( e ).second )
9097         continue; // e is already checked
9098
9099       // check nodes
9100       bool allIn = true;
9101       SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9102       while ( nodeIt->more() && allIn )
9103         allIn = allNodes.count( nodeIt->next() );
9104       if ( allIn )
9105         theElements.insert(e );
9106     }
9107   }
9108
9109   SMESH_MesherHelper helper(*myMesh);
9110   helper.SetIsQuadratic( true );
9111   helper.SetIsBiQuadratic( theToBiQuad );
9112
9113   // add links of quadratic adjacent elements to the helper
9114
9115   if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9116     for ( eIt  = quadAdjacentElems[SMDSAbs_Edge].begin();
9117           eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9118     {
9119       helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9120     }
9121   if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9122     for ( eIt  = quadAdjacentElems[SMDSAbs_Face].begin();
9123           eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9124     {
9125       helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9126     }
9127   if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9128     for ( eIt  = quadAdjacentElems[SMDSAbs_Volume].begin();
9129           eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9130     {
9131       helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9132     }
9133
9134   // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9135
9136   SMESHDS_Mesh*  meshDS = GetMeshDS();
9137   SMESHDS_SubMesh* smDS = 0;
9138   for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9139   {
9140     const SMDS_MeshElement* elem = *eIt;
9141
9142     bool alreadyOK;
9143     int nbCentralNodes = 0;
9144     switch ( elem->GetEntityType() ) {
9145       // linear convertible
9146     case SMDSEntity_Edge:
9147     case SMDSEntity_Triangle:
9148     case SMDSEntity_Quadrangle:
9149     case SMDSEntity_Tetra:
9150     case SMDSEntity_Pyramid:
9151     case SMDSEntity_Hexa:
9152     case SMDSEntity_Penta:             alreadyOK = false;       nbCentralNodes = 0; break;
9153       // quadratic that can become bi-quadratic
9154     case SMDSEntity_Quad_Triangle:
9155     case SMDSEntity_Quad_Quadrangle:
9156     case SMDSEntity_Quad_Hexa:         alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9157       // bi-quadratic
9158     case SMDSEntity_BiQuad_Triangle:
9159     case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9160     case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9161       // the rest
9162     default:                           alreadyOK = true;
9163     }
9164     if ( alreadyOK ) continue;
9165
9166     const SMDSAbs_ElementType type = elem->GetType();
9167     const int                   id = elem->GetID();
9168     const int              nbNodes = elem->NbCornerNodes();
9169     vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9170
9171     helper.SetSubShape( elem->getshapeId() );
9172
9173     if ( !smDS || !smDS->Contains( elem ))
9174       smDS = meshDS->MeshElements( elem->getshapeId() );
9175     meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9176
9177     SMDS_MeshElement * newElem = 0;
9178     switch( nbNodes )
9179     {
9180     case 4: // cases for most frequently used element types go first (for optimization)
9181       if ( type == SMDSAbs_Volume )
9182         newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9183       else
9184         newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9185       break;
9186     case 8:
9187       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9188                                  nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9189       break;
9190     case 3:
9191       newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], id, theForce3d);
9192       break;
9193     case 2:
9194       newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9195       break;
9196     case 5:
9197       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9198                                  nodes[4], id, theForce3d);
9199       break;
9200     case 6:
9201       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9202                                  nodes[4], nodes[5], id, theForce3d);
9203       break;
9204     default:;
9205     }
9206     ReplaceElemInGroups( elem, newElem, meshDS);
9207     if( newElem && smDS )
9208       smDS->AddElement( newElem );
9209
9210     // remove central nodes
9211     for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9212       if ( nodes[i]->NbInverseElements() == 0 )
9213         meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9214
9215   } // loop on theElements
9216
9217   if ( !theForce3d )
9218   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9219     // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9220     // helper.FixQuadraticElements( myError );
9221     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9222   }
9223 }
9224
9225 //=======================================================================
9226 /*!
9227  * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9228  * \return int - nb of checked elements
9229  */
9230 //=======================================================================
9231
9232 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh *    theSm,
9233                                      SMDS_ElemIteratorPtr theItr,
9234                                      const int            /*theShapeID*/)
9235 {
9236   int nbElem = 0;
9237   SMESHDS_Mesh* meshDS = GetMeshDS();
9238   ElemFeatures elemType;
9239   vector<const SMDS_MeshNode *> nodes;
9240
9241   while( theItr->more() )
9242   {
9243     const SMDS_MeshElement* elem = theItr->next();
9244     nbElem++;
9245     if( elem && elem->IsQuadratic())
9246     {
9247       // get elem data
9248       int nbCornerNodes = elem->NbCornerNodes();
9249       nodes.assign( elem->begin_nodes(), elem->end_nodes() );
9250
9251       elemType.Init( elem, /*basicOnly=*/false ).SetID( elem->GetID() ).SetQuad( false );
9252
9253       //remove a quadratic element
9254       if ( !theSm || !theSm->Contains( elem ))
9255         theSm = meshDS->MeshElements( elem->getshapeId() );
9256       meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9257
9258       // remove medium nodes
9259       for ( size_t i = nbCornerNodes; i < nodes.size(); ++i )
9260         if ( nodes[i]->NbInverseElements() == 0 )
9261           meshDS->RemoveFreeNode( nodes[i], theSm );
9262
9263       // add a linear element
9264       nodes.resize( nbCornerNodes );
9265       SMDS_MeshElement * newElem = AddElement( nodes, elemType );
9266       ReplaceElemInGroups(elem, newElem, meshDS);
9267       if( theSm && newElem )
9268         theSm->AddElement( newElem );
9269     }
9270   }
9271   return nbElem;
9272 }
9273
9274 //=======================================================================
9275 //function : ConvertFromQuadratic
9276 //purpose  :
9277 //=======================================================================
9278
9279 bool SMESH_MeshEditor::ConvertFromQuadratic()
9280 {
9281   int nbCheckedElems = 0;
9282   if ( myMesh->HasShapeToMesh() )
9283   {
9284     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9285     {
9286       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9287       while ( smIt->more() ) {
9288         SMESH_subMesh* sm = smIt->next();
9289         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9290           nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9291       }
9292     }
9293   }
9294
9295   int totalNbElems =
9296     GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9297   if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9298   {
9299     SMESHDS_SubMesh *aSM = 0;
9300     removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9301   }
9302
9303   return true;
9304 }
9305
9306 namespace
9307 {
9308   //================================================================================
9309   /*!
9310    * \brief Return true if all medium nodes of the element are in the node set
9311    */
9312   //================================================================================
9313
9314   bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9315   {
9316     for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9317       if ( !nodeSet.count( elem->GetNode(i) ))
9318         return false;
9319     return true;
9320   }
9321 }
9322
9323 //================================================================================
9324 /*!
9325  * \brief Makes given elements linear
9326  */
9327 //================================================================================
9328
9329 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9330 {
9331   if ( theElements.empty() ) return;
9332
9333   // collect IDs of medium nodes of theElements; some of these nodes will be removed
9334   set<int> mediumNodeIDs;
9335   TIDSortedElemSet::iterator eIt = theElements.begin();
9336   for ( ; eIt != theElements.end(); ++eIt )
9337   {
9338     const SMDS_MeshElement* e = *eIt;
9339     for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9340       mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9341   }
9342
9343   // replace given elements by linear ones
9344   SMDS_ElemIteratorPtr elemIt = SMESHUtils::elemSetIterator( theElements );
9345   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9346
9347   // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9348   // except those elements sharing medium nodes of quadratic element whose medium nodes
9349   // are not all in mediumNodeIDs
9350
9351   // get remaining medium nodes
9352   TIDSortedNodeSet mediumNodes;
9353   set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9354   for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9355     if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9356       mediumNodes.insert( mediumNodes.end(), n );
9357
9358   // find more quadratic elements to convert
9359   TIDSortedElemSet moreElemsToConvert;
9360   TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9361   for ( ; nIt != mediumNodes.end(); ++nIt )
9362   {
9363     SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9364     while ( invIt->more() )
9365     {
9366       const SMDS_MeshElement* e = invIt->next();
9367       if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9368       {
9369         // find a more complex element including e and
9370         // whose medium nodes are not in mediumNodes
9371         bool complexFound = false;
9372         for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9373         {
9374           SMDS_ElemIteratorPtr invIt2 =
9375             (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9376           while ( invIt2->more() )
9377           {
9378             const SMDS_MeshElement* eComplex = invIt2->next();
9379             if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9380             {
9381               int nbCommonNodes = SMESH_MeshAlgos::NbCommonNodes( e, eComplex );
9382               if ( nbCommonNodes == e->NbNodes())
9383               {
9384                 complexFound = true;
9385                 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9386                 break;
9387               }
9388             }
9389           }
9390         }
9391         if ( !complexFound )
9392           moreElemsToConvert.insert( e );
9393       }
9394     }
9395   }
9396   elemIt = SMESHUtils::elemSetIterator( moreElemsToConvert );
9397   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9398 }
9399
9400 //=======================================================================
9401 //function : SewSideElements
9402 //purpose  :
9403 //=======================================================================
9404
9405 SMESH_MeshEditor::Sew_Error
9406 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet&    theSide1,
9407                                    TIDSortedElemSet&    theSide2,
9408                                    const SMDS_MeshNode* theFirstNode1,
9409                                    const SMDS_MeshNode* theFirstNode2,
9410                                    const SMDS_MeshNode* theSecondNode1,
9411                                    const SMDS_MeshNode* theSecondNode2)
9412 {
9413   ClearLastCreated();
9414
9415   if ( theSide1.size() != theSide2.size() )
9416     return SEW_DIFF_NB_OF_ELEMENTS;
9417
9418   Sew_Error aResult = SEW_OK;
9419   // Algo:
9420   // 1. Build set of faces representing each side
9421   // 2. Find which nodes of the side 1 to merge with ones on the side 2
9422   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9423
9424   // =======================================================================
9425   // 1. Build set of faces representing each side:
9426   // =======================================================================
9427   // a. build set of nodes belonging to faces
9428   // b. complete set of faces: find missing faces whose nodes are in set of nodes
9429   // c. create temporary faces representing side of volumes if correspondent
9430   //    face does not exist
9431
9432   SMESHDS_Mesh* aMesh = GetMeshDS();
9433   // TODO algorithm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9434   //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9435   TIDSortedElemSet             faceSet1, faceSet2;
9436   set<const SMDS_MeshElement*> volSet1,  volSet2;
9437   set<const SMDS_MeshNode*>    nodeSet1, nodeSet2;
9438   TIDSortedElemSet             * faceSetPtr[] = { &faceSet1, &faceSet2 };
9439   set<const SMDS_MeshElement*> *  volSetPtr[] = { &volSet1,  &volSet2  };
9440   set<const SMDS_MeshNode*>    * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9441   TIDSortedElemSet             * elemSetPtr[] = { &theSide1, &theSide2 };
9442   int iSide, iFace, iNode;
9443
9444   list<const SMDS_MeshElement* > tempFaceList;
9445   for ( iSide = 0; iSide < 2; iSide++ ) {
9446     set<const SMDS_MeshNode*>    * nodeSet = nodeSetPtr[ iSide ];
9447     TIDSortedElemSet             * elemSet = elemSetPtr[ iSide ];
9448     TIDSortedElemSet             * faceSet = faceSetPtr[ iSide ];
9449     set<const SMDS_MeshElement*> * volSet  = volSetPtr [ iSide ];
9450     set<const SMDS_MeshElement*>::iterator vIt;
9451     TIDSortedElemSet::iterator eIt;
9452     set<const SMDS_MeshNode*>::iterator    nIt;
9453
9454     // check that given nodes belong to given elements
9455     const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9456     const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9457     int firstIndex = -1, secondIndex = -1;
9458     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9459       const SMDS_MeshElement* elem = *eIt;
9460       if ( firstIndex  < 0 ) firstIndex  = elem->GetNodeIndex( n1 );
9461       if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9462       if ( firstIndex > -1 && secondIndex > -1 ) break;
9463     }
9464     if ( firstIndex < 0 || secondIndex < 0 ) {
9465       // we can simply return until temporary faces created
9466       return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9467     }
9468
9469     // -----------------------------------------------------------
9470     // 1a. Collect nodes of existing faces
9471     //     and build set of face nodes in order to detect missing
9472     //     faces corresponding to sides of volumes
9473     // -----------------------------------------------------------
9474
9475     set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9476
9477     // loop on the given element of a side
9478     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9479       //const SMDS_MeshElement* elem = *eIt;
9480       const SMDS_MeshElement* elem = *eIt;
9481       if ( elem->GetType() == SMDSAbs_Face ) {
9482         faceSet->insert( elem );
9483         set <const SMDS_MeshNode*> faceNodeSet;
9484         SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9485         while ( nodeIt->more() ) {
9486           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9487           nodeSet->insert( n );
9488           faceNodeSet.insert( n );
9489         }
9490         setOfFaceNodeSet.insert( faceNodeSet );
9491       }
9492       else if ( elem->GetType() == SMDSAbs_Volume )
9493         volSet->insert( elem );
9494     }
9495     // ------------------------------------------------------------------------------
9496     // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9497     // ------------------------------------------------------------------------------
9498
9499     for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9500       SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9501       while ( fIt->more() ) { // loop on faces sharing a node
9502         const SMDS_MeshElement* f = fIt->next();
9503         if ( faceSet->find( f ) == faceSet->end() ) {
9504           // check if all nodes are in nodeSet and
9505           // complete setOfFaceNodeSet if they are
9506           set <const SMDS_MeshNode*> faceNodeSet;
9507           SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9508           bool allInSet = true;
9509           while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9510             const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9511             if ( nodeSet->find( n ) == nodeSet->end() )
9512               allInSet = false;
9513             else
9514               faceNodeSet.insert( n );
9515           }
9516           if ( allInSet ) {
9517             faceSet->insert( f );
9518             setOfFaceNodeSet.insert( faceNodeSet );
9519           }
9520         }
9521       }
9522     }
9523
9524     // -------------------------------------------------------------------------
9525     // 1c. Create temporary faces representing sides of volumes if correspondent
9526     //     face does not exist
9527     // -------------------------------------------------------------------------
9528
9529     if ( !volSet->empty() ) {
9530       //int nodeSetSize = nodeSet->size();
9531
9532       // loop on given volumes
9533       for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
9534         SMDS_VolumeTool vol (*vIt);
9535         // loop on volume faces: find free faces
9536         // --------------------------------------
9537         list<const SMDS_MeshElement* > freeFaceList;
9538         for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
9539           if ( !vol.IsFreeFace( iFace ))
9540             continue;
9541           // check if there is already a face with same nodes in a face set
9542           const SMDS_MeshElement* aFreeFace = 0;
9543           const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
9544           int nbNodes = vol.NbFaceNodes( iFace );
9545           set <const SMDS_MeshNode*> faceNodeSet;
9546           vol.GetFaceNodes( iFace, faceNodeSet );
9547           bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
9548           if ( isNewFace ) {
9549             // no such a face is given but it still can exist, check it
9550             vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
9551             aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
9552           }
9553           if ( !aFreeFace ) {
9554             // create a temporary face
9555             if ( nbNodes == 3 ) {
9556               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
9557               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
9558             }
9559             else if ( nbNodes == 4 ) {
9560               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9561               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9562             }
9563             else {
9564               vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9565               //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
9566               aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
9567             }
9568             if ( aFreeFace )
9569               tempFaceList.push_back( aFreeFace );
9570           }
9571
9572           if ( aFreeFace )
9573             freeFaceList.push_back( aFreeFace );
9574
9575         } // loop on faces of a volume
9576
9577         // choose one of several free faces of a volume
9578         // --------------------------------------------
9579         if ( freeFaceList.size() > 1 ) {
9580           // choose a face having max nb of nodes shared by other elems of a side
9581           int maxNbNodes = -1;
9582           list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
9583           while ( fIt != freeFaceList.end() ) { // loop on free faces
9584             int nbSharedNodes = 0;
9585             SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9586             while ( nodeIt->more() ) { // loop on free face nodes
9587               const SMDS_MeshNode* n =
9588                 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9589               SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
9590               while ( invElemIt->more() ) {
9591                 const SMDS_MeshElement* e = invElemIt->next();
9592                 nbSharedNodes += faceSet->count( e );
9593                 nbSharedNodes += elemSet->count( e );
9594               }
9595             }
9596             if ( nbSharedNodes > maxNbNodes ) {
9597               maxNbNodes = nbSharedNodes;
9598               freeFaceList.erase( freeFaceList.begin(), fIt++ );
9599             }
9600             else if ( nbSharedNodes == maxNbNodes ) {
9601               fIt++;
9602             }
9603             else {
9604               freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
9605             }
9606           }
9607           if ( freeFaceList.size() > 1 )
9608           {
9609             // could not choose one face, use another way
9610             // choose a face most close to the bary center of the opposite side
9611             gp_XYZ aBC( 0., 0., 0. );
9612             set <const SMDS_MeshNode*> addedNodes;
9613             TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
9614             eIt = elemSet2->begin();
9615             for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
9616               SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
9617               while ( nodeIt->more() ) { // loop on free face nodes
9618                 const SMDS_MeshNode* n =
9619                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9620                 if ( addedNodes.insert( n ).second )
9621                   aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
9622               }
9623             }
9624             aBC /= addedNodes.size();
9625             double minDist = DBL_MAX;
9626             fIt = freeFaceList.begin();
9627             while ( fIt != freeFaceList.end() ) { // loop on free faces
9628               double dist = 0;
9629               SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9630               while ( nodeIt->more() ) { // loop on free face nodes
9631                 const SMDS_MeshNode* n =
9632                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9633                 gp_XYZ p( n->X(),n->Y(),n->Z() );
9634                 dist += ( aBC - p ).SquareModulus();
9635               }
9636               if ( dist < minDist ) {
9637                 minDist = dist;
9638                 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9639               }
9640               else
9641                 fIt = freeFaceList.erase( fIt++ );
9642             }
9643           }
9644         } // choose one of several free faces of a volume
9645
9646         if ( freeFaceList.size() == 1 ) {
9647           const SMDS_MeshElement* aFreeFace = freeFaceList.front();
9648           faceSet->insert( aFreeFace );
9649           // complete a node set with nodes of a found free face
9650           //           for ( iNode = 0; iNode < ; iNode++ )
9651           //             nodeSet->insert( fNodes[ iNode ] );
9652         }
9653
9654       } // loop on volumes of a side
9655
9656       //       // complete a set of faces if new nodes in a nodeSet appeared
9657       //       // ----------------------------------------------------------
9658       //       if ( nodeSetSize != nodeSet->size() ) {
9659       //         for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9660       //           SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9661       //           while ( fIt->more() ) { // loop on faces sharing a node
9662       //             const SMDS_MeshElement* f = fIt->next();
9663       //             if ( faceSet->find( f ) == faceSet->end() ) {
9664       //               // check if all nodes are in nodeSet and
9665       //               // complete setOfFaceNodeSet if they are
9666       //               set <const SMDS_MeshNode*> faceNodeSet;
9667       //               SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9668       //               bool allInSet = true;
9669       //               while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9670       //                 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9671       //                 if ( nodeSet->find( n ) == nodeSet->end() )
9672       //                   allInSet = false;
9673       //                 else
9674       //                   faceNodeSet.insert( n );
9675       //               }
9676       //               if ( allInSet ) {
9677       //                 faceSet->insert( f );
9678       //                 setOfFaceNodeSet.insert( faceNodeSet );
9679       //               }
9680       //             }
9681       //           }
9682       //         }
9683       //       }
9684     } // Create temporary faces, if there are volumes given
9685   } // loop on sides
9686
9687   if ( faceSet1.size() != faceSet2.size() ) {
9688     // delete temporary faces: they are in reverseElements of actual nodes
9689     //    SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9690     //    while ( tmpFaceIt->more() )
9691     //      aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9692     //    list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9693     //    for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9694     //      aMesh->RemoveElement(*tmpFaceIt);
9695     MESSAGE("Diff nb of faces");
9696     return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9697   }
9698
9699   // ============================================================
9700   // 2. Find nodes to merge:
9701   //              bind a node to remove to a node to put instead
9702   // ============================================================
9703
9704   TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
9705   if ( theFirstNode1 != theFirstNode2 )
9706     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9707   if ( theSecondNode1 != theSecondNode2 )
9708     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9709
9710   LinkID_Gen aLinkID_Gen( GetMeshDS() );
9711   set< long > linkIdSet; // links to process
9712   linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
9713
9714   typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
9715   list< NLink > linkList[2];
9716   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9717   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9718   // loop on links in linkList; find faces by links and append links
9719   // of the found faces to linkList
9720   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9721   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
9722   {
9723     NLink link[] = { *linkIt[0], *linkIt[1] };
9724     long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
9725     if ( !linkIdSet.count( linkID ) )
9726       continue;
9727
9728     // by links, find faces in the face sets,
9729     // and find indices of link nodes in the found faces;
9730     // in a face set, there is only one or no face sharing a link
9731     // ---------------------------------------------------------------
9732
9733     const SMDS_MeshElement* face[] = { 0, 0 };
9734     vector<const SMDS_MeshNode*> fnodes[2];
9735     int iLinkNode[2][2];
9736     TIDSortedElemSet avoidSet;
9737     for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9738       const SMDS_MeshNode* n1 = link[iSide].first;
9739       const SMDS_MeshNode* n2 = link[iSide].second;
9740       //cout << "Side " << iSide << " ";
9741       //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
9742       // find a face by two link nodes
9743       face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
9744                                                       *faceSetPtr[ iSide ], avoidSet,
9745                                                       &iLinkNode[iSide][0],
9746                                                       &iLinkNode[iSide][1] );
9747       if ( face[ iSide ])
9748       {
9749         //cout << " F " << face[ iSide]->GetID() <<endl;
9750         faceSetPtr[ iSide ]->erase( face[ iSide ]);
9751         // put face nodes to fnodes
9752         SMDS_MeshElement::iterator nIt( face[ iSide ]->interlacedNodesIterator() ), nEnd;
9753         fnodes[ iSide ].assign( nIt, nEnd );
9754         fnodes[ iSide ].push_back( fnodes[ iSide ].front());
9755       }
9756     }
9757
9758     // check similarity of elements of the sides
9759     if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
9760       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
9761       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
9762         aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9763       }
9764       else {
9765         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9766       }
9767       break; // do not return because it's necessary to remove tmp faces
9768     }
9769
9770     // set nodes to merge
9771     // -------------------
9772
9773     if ( face[0] && face[1] )  {
9774       const int nbNodes = face[0]->NbNodes();
9775       if ( nbNodes != face[1]->NbNodes() ) {
9776         MESSAGE("Diff nb of face nodes");
9777         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9778         break; // do not return because it s necessary to remove tmp faces
9779       }
9780       bool reverse[] = { false, false }; // order of nodes in the link
9781       for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9782         // analyse link orientation in faces
9783         int i1 = iLinkNode[ iSide ][ 0 ];
9784         int i2 = iLinkNode[ iSide ][ 1 ];
9785         reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
9786       }
9787       int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
9788       int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
9789       for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
9790       {
9791         nReplaceMap.insert  ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
9792                                           fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
9793       }
9794
9795       // add other links of the faces to linkList
9796       // -----------------------------------------
9797
9798       for ( iNode = 0; iNode < nbNodes; iNode++ )  {
9799         linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
9800         pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
9801         if ( !iter_isnew.second ) { // already in a set: no need to process
9802           linkIdSet.erase( iter_isnew.first );
9803         }
9804         else // new in set == encountered for the first time: add
9805         {
9806           const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
9807           const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
9808           linkList[0].push_back ( NLink( n1, n2 ));
9809           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
9810         }
9811       }
9812     } // 2 faces found
9813
9814     if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
9815       break;
9816
9817   } // loop on link lists
9818
9819   if ( aResult == SEW_OK &&
9820        ( //linkIt[0] != linkList[0].end() ||
9821         !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
9822     MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
9823              " " << (faceSetPtr[1]->empty()));
9824     aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9825   }
9826
9827   // ====================================================================
9828   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9829   // ====================================================================
9830
9831   // delete temporary faces
9832   //  SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9833   //  while ( tmpFaceIt->more() )
9834   //    aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9835   list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9836   for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9837     aMesh->RemoveElement(*tmpFaceIt);
9838
9839   if ( aResult != SEW_OK)
9840     return aResult;
9841
9842   list< int > nodeIDsToRemove;
9843   vector< const SMDS_MeshNode*> nodes;
9844   ElemFeatures elemType;
9845
9846   // loop on nodes replacement map
9847   TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
9848   for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
9849     if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second )
9850     {
9851       const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
9852       nodeIDsToRemove.push_back( nToRemove->GetID() );
9853       // loop on elements sharing nToRemove
9854       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
9855       while ( invElemIt->more() ) {
9856         const SMDS_MeshElement* e = invElemIt->next();
9857         // get a new suite of nodes: make replacement
9858         int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
9859         nodes.resize( nbNodes );
9860         SMDS_ElemIteratorPtr nIt = e->nodesIterator();
9861         while ( nIt->more() ) {
9862           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
9863           nnIt = nReplaceMap.find( n );
9864           if ( nnIt != nReplaceMap.end() ) {
9865             nbReplaced++;
9866             n = (*nnIt).second;
9867           }
9868           nodes[ i++ ] = n;
9869         }
9870         //       if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
9871         //         elemIDsToRemove.push_back( e->GetID() );
9872         //       else
9873         if ( nbReplaced )
9874         {
9875           elemType.Init( e, /*basicOnly=*/false ).SetID( e->GetID() );
9876           aMesh->RemoveElement( e );
9877
9878           if ( SMDS_MeshElement* newElem = this->AddElement( nodes, elemType ))
9879           {
9880             AddToSameGroups( newElem, e, aMesh );
9881             if ( int aShapeId = e->getshapeId() )
9882               aMesh->SetMeshElementOnShape( newElem, aShapeId );
9883           }
9884         }
9885       }
9886     }
9887
9888   Remove( nodeIDsToRemove, true );
9889
9890   return aResult;
9891 }
9892
9893 //================================================================================
9894 /*!
9895  * \brief Find corresponding nodes in two sets of faces
9896  * \param theSide1 - first face set
9897  * \param theSide2 - second first face
9898  * \param theFirstNode1 - a boundary node of set 1
9899  * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
9900  * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
9901  * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
9902  * \param nReplaceMap - output map of corresponding nodes
9903  * \return bool  - is a success or not
9904  */
9905 //================================================================================
9906
9907 #ifdef _DEBUG_
9908 //#define DEBUG_MATCHING_NODES
9909 #endif
9910
9911 SMESH_MeshEditor::Sew_Error
9912 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
9913                                     set<const SMDS_MeshElement*>& theSide2,
9914                                     const SMDS_MeshNode*          theFirstNode1,
9915                                     const SMDS_MeshNode*          theFirstNode2,
9916                                     const SMDS_MeshNode*          theSecondNode1,
9917                                     const SMDS_MeshNode*          theSecondNode2,
9918                                     TNodeNodeMap &                nReplaceMap)
9919 {
9920   set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
9921
9922   nReplaceMap.clear();
9923   //if ( theFirstNode1 != theFirstNode2 )
9924   nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9925   //if ( theSecondNode1 != theSecondNode2 )
9926   nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9927
9928   set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
9929   linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
9930
9931   list< NLink > linkList[2];
9932   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9933   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9934
9935   // loop on links in linkList; find faces by links and append links
9936   // of the found faces to linkList
9937   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9938   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
9939     NLink link[] = { *linkIt[0], *linkIt[1] };
9940     if ( linkSet.find( link[0] ) == linkSet.end() )
9941       continue;
9942
9943     // by links, find faces in the face sets,
9944     // and find indices of link nodes in the found faces;
9945     // in a face set, there is only one or no face sharing a link
9946     // ---------------------------------------------------------------
9947
9948     const SMDS_MeshElement* face[] = { 0, 0 };
9949     list<const SMDS_MeshNode*> notLinkNodes[2];
9950     //bool reverse[] = { false, false }; // order of notLinkNodes
9951     int nbNodes[2];
9952     for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
9953     {
9954       const SMDS_MeshNode* n1 = link[iSide].first;
9955       const SMDS_MeshNode* n2 = link[iSide].second;
9956       set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
9957       set< const SMDS_MeshElement* > facesOfNode1;
9958       for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
9959       {
9960         // during a loop of the first node, we find all faces around n1,
9961         // during a loop of the second node, we find one face sharing both n1 and n2
9962         const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
9963         SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
9964         while ( fIt->more() ) { // loop on faces sharing a node
9965           const SMDS_MeshElement* f = fIt->next();
9966           if (faceSet->find( f ) != faceSet->end() && // f is in face set
9967               ! facesOfNode1.insert( f ).second ) // f encounters twice
9968           {
9969             if ( face[ iSide ] ) {
9970               MESSAGE( "2 faces per link " );
9971               return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9972             }
9973             face[ iSide ] = f;
9974             faceSet->erase( f );
9975
9976             // get not link nodes
9977             int nbN = f->NbNodes();
9978             if ( f->IsQuadratic() )
9979               nbN /= 2;
9980             nbNodes[ iSide ] = nbN;
9981             list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
9982             int i1 = f->GetNodeIndex( n1 );
9983             int i2 = f->GetNodeIndex( n2 );
9984             int iEnd = nbN, iBeg = -1, iDelta = 1;
9985             bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
9986             if ( reverse ) {
9987               std::swap( iEnd, iBeg ); iDelta = -1;
9988             }
9989             int i = i2;
9990             while ( true ) {
9991               i += iDelta;
9992               if ( i == iEnd ) i = iBeg + iDelta;
9993               if ( i == i1 ) break;
9994               nodes.push_back ( f->GetNode( i ) );
9995             }
9996           }
9997         }
9998       }
9999     }
10000     // check similarity of elements of the sides
10001     if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10002       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10003       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10004         return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10005       }
10006       else {
10007         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10008       }
10009     }
10010
10011     // set nodes to merge
10012     // -------------------
10013
10014     if ( face[0] && face[1] )  {
10015       if ( nbNodes[0] != nbNodes[1] ) {
10016         MESSAGE("Diff nb of face nodes");
10017         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10018       }
10019 #ifdef DEBUG_MATCHING_NODES
10020       MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10021                 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10022                 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10023 #endif
10024       int nbN = nbNodes[0];
10025       {
10026         list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10027         list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10028         for ( int i = 0 ; i < nbN - 2; ++i ) {
10029 #ifdef DEBUG_MATCHING_NODES
10030           MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10031 #endif
10032           nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10033         }
10034       }
10035
10036       // add other links of the face 1 to linkList
10037       // -----------------------------------------
10038
10039       const SMDS_MeshElement* f0 = face[0];
10040       const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10041       for ( int i = 0; i < nbN; i++ )
10042       {
10043         const SMDS_MeshNode* n2 = f0->GetNode( i );
10044         pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10045           linkSet.insert( SMESH_TLink( n1, n2 ));
10046         if ( !iter_isnew.second ) { // already in a set: no need to process
10047           linkSet.erase( iter_isnew.first );
10048         }
10049         else // new in set == encountered for the first time: add
10050         {
10051 #ifdef DEBUG_MATCHING_NODES
10052           MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10053                     << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10054 #endif
10055           linkList[0].push_back ( NLink( n1, n2 ));
10056           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10057         }
10058         n1 = n2;
10059       }
10060     } // 2 faces found
10061   } // loop on link lists
10062
10063   return SEW_OK;
10064 }
10065
10066 namespace // automatically find theAffectedElems for DoubleNodes()
10067 {
10068   bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem );
10069
10070   //--------------------------------------------------------------------------------
10071   // Nodes shared by adjacent FissureBorder's.
10072   // 1 node  if FissureBorder separates faces
10073   // 2 nodes if FissureBorder separates volumes
10074   struct SubBorder
10075   {
10076     const SMDS_MeshNode* _nodes[2];
10077     int                  _nbNodes;
10078
10079     SubBorder( const SMDS_MeshNode* n1, const SMDS_MeshNode* n2 = 0 )
10080     {
10081       _nodes[0] = n1;
10082       _nodes[1] = n2;
10083       _nbNodes = bool( n1 ) + bool( n2 );
10084       if ( _nbNodes == 2 && n1 > n2 )
10085         std::swap( _nodes[0], _nodes[1] );
10086     }
10087     bool operator<( const SubBorder& other ) const
10088     {
10089       for ( int i = 0; i < _nbNodes; ++i )
10090       {
10091         if ( _nodes[i] < other._nodes[i] ) return true;
10092         if ( _nodes[i] > other._nodes[i] ) return false;
10093       }
10094       return false;
10095     }
10096   };
10097
10098   //--------------------------------------------------------------------------------
10099   // Map a SubBorder to all FissureBorder it bounds
10100   struct FissureBorder;
10101   typedef std::map< SubBorder, std::vector< FissureBorder* > > TBorderLinks;
10102   typedef TBorderLinks::iterator                               TMappedSub;
10103
10104   //--------------------------------------------------------------------------------
10105   /*!
10106    * \brief Element border (volume facet or face edge) at a fissure
10107    */
10108   struct FissureBorder
10109   {
10110     std::vector< const SMDS_MeshNode* > _nodes;    // border nodes
10111     const SMDS_MeshElement*             _elems[2]; // volume or face adjacent to fissure
10112
10113     std::vector< TMappedSub           > _mappedSubs;  // Sub() in TBorderLinks map
10114     std::vector< const SMDS_MeshNode* > _sortedNodes; // to compare FissureBorder's
10115
10116     FissureBorder( FissureBorder && from ) // move constructor
10117     {
10118       std::swap( _nodes,       from._nodes );
10119       std::swap( _sortedNodes, from._sortedNodes );
10120       _elems[0] = from._elems[0];
10121       _elems[1] = from._elems[1];
10122     }
10123
10124     FissureBorder( const SMDS_MeshElement*                  elemToDuplicate,
10125                    std::vector< const SMDS_MeshElement* > & adjElems)
10126       : _nodes( elemToDuplicate->NbCornerNodes() )
10127     {
10128       for ( size_t i = 0; i < _nodes.size(); ++i )
10129         _nodes[i] = elemToDuplicate->GetNode( i );
10130
10131       SMDSAbs_ElementType type = SMDSAbs_ElementType( elemToDuplicate->GetType() + 1 );
10132       findAdjacent( type, adjElems );
10133     }
10134
10135     FissureBorder( const SMDS_MeshNode**                    nodes,
10136                    const size_t                             nbNodes,
10137                    const SMDSAbs_ElementType                adjElemsType,
10138                    std::vector< const SMDS_MeshElement* > & adjElems)
10139       : _nodes( nodes, nodes + nbNodes )
10140     {
10141       findAdjacent( adjElemsType, adjElems );
10142     }
10143
10144     void findAdjacent( const SMDSAbs_ElementType                adjElemsType,
10145                        std::vector< const SMDS_MeshElement* > & adjElems)
10146     {
10147       _elems[0] = _elems[1] = 0;
10148       adjElems.clear();
10149       if ( SMDS_Mesh::GetElementsByNodes( _nodes, adjElems, adjElemsType ))
10150         for ( size_t i = 0; i < adjElems.size() && i < 2; ++i )
10151           _elems[i] = adjElems[i];
10152     }
10153
10154     bool operator<( const FissureBorder& other ) const
10155     {
10156       return GetSortedNodes() < other.GetSortedNodes();
10157     }
10158
10159     const std::vector< const SMDS_MeshNode* >& GetSortedNodes() const
10160     {
10161       if ( _sortedNodes.empty() && !_nodes.empty() )
10162       {
10163         FissureBorder* me = const_cast<FissureBorder*>( this );
10164         me->_sortedNodes = me->_nodes;
10165         std::sort( me->_sortedNodes.begin(), me->_sortedNodes.end() );
10166       }
10167       return _sortedNodes;
10168     }
10169
10170     size_t NbSub() const
10171     {
10172       return _nodes.size();
10173     }
10174
10175     SubBorder Sub(size_t i) const
10176     {
10177       return SubBorder( _nodes[i], NbSub() > 2 ? _nodes[ (i+1)%NbSub() ] : 0 );
10178     }
10179
10180     void AddSelfTo( TBorderLinks& borderLinks )
10181     {
10182       _mappedSubs.resize( NbSub() );
10183       for ( size_t i = 0; i < NbSub(); ++i )
10184       {
10185         TBorderLinks::iterator s2b =
10186           borderLinks.insert( std::make_pair( Sub(i), TBorderLinks::mapped_type() )).first;
10187         s2b->second.push_back( this );
10188         _mappedSubs[ i ] = s2b;
10189       }
10190     }
10191
10192     void Clear()
10193     {
10194       _nodes.clear();
10195     }
10196
10197     const SMDS_MeshElement* GetMarkedElem() const
10198     {
10199       if ( _nodes.empty() ) return 0; // cleared
10200       if ( _elems[0] && _elems[0]->isMarked() ) return _elems[0];
10201       if ( _elems[1] && _elems[1]->isMarked() ) return _elems[1];
10202       return 0;
10203     }
10204
10205     gp_XYZ GetNorm() const // normal to the border
10206     {
10207       gp_XYZ norm;
10208       if ( _nodes.size() == 2 )
10209       {
10210         gp_XYZ avgNorm( 0,0,0 ); // sum of normals of adjacent faces
10211         if ( SMESH_MeshAlgos::FaceNormal( _elems[0], norm ))
10212           avgNorm += norm;
10213         if ( SMESH_MeshAlgos::FaceNormal( _elems[1], norm ))
10214           avgNorm += norm;
10215
10216         gp_XYZ bordDir( SMESH_NodeXYZ( this->_nodes[0] ) - SMESH_NodeXYZ( this->_nodes[1] ));
10217         norm = bordDir ^ avgNorm;
10218       }
10219       else
10220       {
10221         SMESH_NodeXYZ p0( _nodes[0] );
10222         SMESH_NodeXYZ p1( _nodes[1] );
10223         SMESH_NodeXYZ p2( _nodes[2] );
10224         norm = ( p0 - p1 ) ^ ( p2 - p1 );
10225       }
10226       if ( isOut( _nodes[0], norm, GetMarkedElem() ))
10227         norm.Reverse();
10228
10229       return norm;
10230     }
10231
10232     void ChooseSide() // mark an _elem located at positive side of fissure
10233     {
10234       _elems[0]->setIsMarked( true );
10235       gp_XYZ norm = GetNorm();
10236       double maxX = norm.Coord(1);
10237       if ( Abs( maxX ) < Abs( norm.Coord(2)) ) maxX = norm.Coord(2);
10238       if ( Abs( maxX ) < Abs( norm.Coord(3)) ) maxX = norm.Coord(3);
10239       if ( maxX < 0 )
10240       {
10241         _elems[0]->setIsMarked( false );
10242         _elems[1]->setIsMarked( true );
10243       }
10244     }
10245
10246   }; // struct FissureBorder
10247
10248   //--------------------------------------------------------------------------------
10249   /*!
10250    * \brief Classifier of elements at fissure edge
10251    */
10252   class FissureNormal
10253   {
10254     std::vector< gp_XYZ > _normals;
10255     bool                  _bothIn;
10256
10257   public:
10258     void Add( const SMDS_MeshNode* n, const FissureBorder& bord )
10259     {
10260       _bothIn = false;
10261       _normals.reserve(2);
10262       _normals.push_back( bord.GetNorm() );
10263       if ( _normals.size() == 2 )
10264         _bothIn = !isOut( n, _normals[0], bord.GetMarkedElem() );
10265     }
10266
10267     bool IsIn( const SMDS_MeshNode* n, const SMDS_MeshElement* elem ) const
10268     {
10269       bool isIn = false;
10270       switch ( _normals.size() ) {
10271       case 1:
10272       {
10273         isIn = !isOut( n, _normals[0], elem );
10274         break;
10275       }
10276       case 2:
10277       {
10278         bool in1 = !isOut( n, _normals[0], elem );
10279         bool in2 = !isOut( n, _normals[1], elem );
10280         isIn = _bothIn ? ( in1 && in2 ) : ( in1 || in2 );
10281       }
10282       }
10283       return isIn;
10284     }
10285   };
10286
10287   //================================================================================
10288   /*!
10289    * \brief Classify an element by a plane passing through a node
10290    */
10291   //================================================================================
10292
10293   bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem )
10294   {
10295     SMESH_NodeXYZ p = n;
10296     double sumDot = 0;
10297     for ( int i = 0, nb = elem->NbCornerNodes(); i < nb; ++i )
10298     {
10299       SMESH_NodeXYZ pi = elem->GetNode( i );
10300       sumDot += norm * ( pi - p );
10301     }
10302     return sumDot < -1e-100;
10303   }
10304
10305   //================================================================================
10306   /*!
10307    * \brief Find FissureBorder's by nodes to duplicate
10308    */
10309   //================================================================================
10310
10311   void findFissureBorders( const TIDSortedElemSet&        theNodes,
10312                            std::vector< FissureBorder > & theFissureBorders )
10313   {
10314     TIDSortedElemSet::const_iterator nIt = theNodes.begin();
10315     const SMDS_MeshNode* n = dynamic_cast< const SMDS_MeshNode*>( *nIt );
10316     if ( !n ) return;
10317     SMDSAbs_ElementType elemType = SMDSAbs_Volume;
10318     if ( n->NbInverseElements( elemType ) == 0 )
10319     {
10320       elemType = SMDSAbs_Face;
10321       if ( n->NbInverseElements( elemType ) == 0 )
10322         return;
10323     }
10324     // unmark elements touching the fissure
10325     for ( ; nIt != theNodes.end(); ++nIt )
10326       SMESH_MeshAlgos::MarkElems( cast2Node(*nIt)->GetInverseElementIterator(), false );
10327
10328     // loop on elements touching the fissure to get their borders belonging to the fissure
10329     std::set< FissureBorder >              fissureBorders;
10330     std::vector< const SMDS_MeshElement* > adjElems;
10331     std::vector< const SMDS_MeshNode* >    nodes;
10332     SMDS_VolumeTool volTool;
10333     for ( nIt = theNodes.begin(); nIt != theNodes.end(); ++nIt )
10334     {
10335       SMDS_ElemIteratorPtr invIt = cast2Node(*nIt)->GetInverseElementIterator( elemType );
10336       while ( invIt->more() )
10337       {
10338         const SMDS_MeshElement* eInv = invIt->next();
10339         if ( eInv->isMarked() ) continue;
10340         eInv->setIsMarked( true );
10341
10342         if ( elemType == SMDSAbs_Volume )
10343         {
10344           volTool.Set( eInv );
10345           int iQuad = eInv->IsQuadratic() ? 2 : 1;
10346           for ( int iF = 0, nbF = volTool.NbFaces(); iF < nbF; ++iF )
10347           {
10348             const SMDS_MeshNode** nn = volTool.GetFaceNodes( iF );
10349             int                  nbN = volTool.NbFaceNodes( iF ) / iQuad;
10350             nodes.clear();
10351             bool allOnFissure = true;
10352             for ( int iN = 0; iN < nbN  && allOnFissure; iN += iQuad )
10353               if (( allOnFissure = theNodes.count( nn[ iN ])))
10354                 nodes.push_back( nn[ iN ]);
10355             if ( allOnFissure )
10356               fissureBorders.insert( std::move( FissureBorder( &nodes[0], nodes.size(),
10357                                                                elemType, adjElems )));
10358           }
10359         }
10360         else // elemType == SMDSAbs_Face
10361         {
10362           const SMDS_MeshNode* nn[2] = { eInv->GetNode( eInv->NbCornerNodes()-1 ), 0 };
10363           bool            onFissure0 = theNodes.count( nn[0] ), onFissure1;
10364           for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN; ++iN )
10365           {
10366             nn[1]      = eInv->GetNode( iN );
10367             onFissure1 = theNodes.count( nn[1] );
10368             if ( onFissure0 && onFissure1 )
10369               fissureBorders.insert( std::move( FissureBorder( nn, 2, elemType, adjElems )));
10370             nn[0]      = nn[1];
10371             onFissure0 = onFissure1;
10372           }
10373         }
10374       }
10375     }
10376
10377     theFissureBorders.reserve( theFissureBorders.size() + fissureBorders.size());
10378     std::set< FissureBorder >::iterator bord = fissureBorders.begin();
10379     for ( ; bord != fissureBorders.end(); ++bord )
10380     {
10381       theFissureBorders.push_back( std::move( const_cast<FissureBorder&>( *bord ) ));
10382     }
10383     return;
10384   } // findFissureBorders()
10385
10386   //================================================================================
10387   /*!
10388    * \brief Find elements on one side of a fissure defined by elements or nodes to duplicate
10389    *  \param [in] theElemsOrNodes - elements or nodes to duplicate
10390    *  \param [in] theNodesNot - nodes not to duplicate
10391    *  \param [out] theAffectedElems - the found elements
10392    */
10393   //================================================================================
10394
10395   void findAffectedElems( const TIDSortedElemSet& theElemsOrNodes,
10396                           TIDSortedElemSet&       theAffectedElems)
10397   {
10398     if ( theElemsOrNodes.empty() ) return;
10399
10400     // find FissureBorder's
10401
10402     std::vector< FissureBorder >           fissure;
10403     std::vector< const SMDS_MeshElement* > elemsByFacet;
10404
10405     TIDSortedElemSet::const_iterator elIt = theElemsOrNodes.begin();
10406     if ( (*elIt)->GetType() == SMDSAbs_Node )
10407     {
10408       findFissureBorders( theElemsOrNodes, fissure );
10409     }
10410     else
10411     {
10412       fissure.reserve( theElemsOrNodes.size() );
10413       for ( ; elIt != theElemsOrNodes.end(); ++elIt )
10414         fissure.push_back( std::move( FissureBorder( *elIt, elemsByFacet )));
10415     }
10416     if ( fissure.empty() )
10417       return;
10418
10419     // fill borderLinks
10420
10421     TBorderLinks borderLinks;
10422
10423     for ( size_t i = 0; i < fissure.size(); ++i )
10424     {
10425       fissure[i].AddSelfTo( borderLinks );
10426     }
10427
10428     // get theAffectedElems
10429
10430     // unmark elements having nodes on the fissure, theAffectedElems elements will be marked
10431     for ( size_t i = 0; i < fissure.size(); ++i )
10432       for ( size_t j = 0; j < fissure[i]._nodes.size(); ++j )
10433       {
10434         SMESH_MeshAlgos::MarkElemNodes( fissure[i]._nodes[j]->GetInverseElementIterator(),
10435                                         false, /*markElem=*/true );
10436       }
10437
10438     std::vector<const SMDS_MeshNode *>                 facetNodes;
10439     std::map< const SMDS_MeshNode*, FissureNormal >    fissEdgeNodes2Norm;
10440     boost::container::flat_set< const SMDS_MeshNode* > fissureNodes;
10441
10442     // choose a side of fissure
10443     fissure[0].ChooseSide();
10444     theAffectedElems.insert( fissure[0].GetMarkedElem() );
10445
10446     size_t nbCheckedBorders = 0;
10447     while ( nbCheckedBorders < fissure.size() )
10448     {
10449       // find a FissureBorder to treat
10450       FissureBorder* bord = 0;
10451       for ( size_t i = 0; i < fissure.size()  && !bord; ++i )
10452         if ( fissure[i].GetMarkedElem() )
10453           bord = & fissure[i];
10454       for ( size_t i = 0; i < fissure.size()  && !bord; ++i )
10455         if ( fissure[i].NbSub() > 0 && fissure[i]._elems[0] )
10456         {
10457           bord = & fissure[i];
10458           bord->ChooseSide();
10459           theAffectedElems.insert( bord->GetMarkedElem() );
10460         }
10461       if ( !bord ) return;
10462       ++nbCheckedBorders;
10463
10464       // treat FissureBorder's linked to bord
10465       fissureNodes.clear();
10466       fissureNodes.insert( bord->_nodes.begin(), bord->_nodes.end() );
10467       for ( size_t i = 0; i < bord->NbSub(); ++i )
10468       {
10469         TBorderLinks::iterator l2b = bord->_mappedSubs[ i ];
10470         if ( l2b == borderLinks.end() || l2b->second.empty() ) continue;
10471         std::vector< FissureBorder* >& linkedBorders = l2b->second;
10472         const SubBorder&                          sb = l2b->first;
10473         const SMDS_MeshElement*             bordElem = bord->GetMarkedElem();
10474
10475         if ( linkedBorders.size() == 1 ) // fissure edge reached, fill fissEdgeNodes2Norm
10476         {
10477           for ( int j = 0; j < sb._nbNodes; ++j )
10478             fissEdgeNodes2Norm[ sb._nodes[j] ].Add( sb._nodes[j], *bord );
10479           continue;
10480         }
10481
10482         // add to theAffectedElems elems sharing nodes of a SubBorder and a node of bordElem
10483         // until an elem adjacent to a neighbour FissureBorder is found
10484         facetNodes.clear();
10485         facetNodes.insert( facetNodes.end(), sb._nodes, sb._nodes + sb._nbNodes );
10486         facetNodes.resize( sb._nbNodes + 1 );
10487
10488         while ( bordElem )
10489         {
10490           // check if bordElem is adjacent to a neighbour FissureBorder
10491           for ( size_t j = 0; j < linkedBorders.size(); ++j )
10492           {
10493             FissureBorder* bord2 = linkedBorders[j];
10494             if ( bord2 == bord ) continue;
10495             if ( bordElem == bord2->_elems[0] || bordElem == bord2->_elems[1] )
10496               bordElem = 0;
10497             else
10498               fissureNodes.insert( bord2->_nodes.begin(), bord2->_nodes.end() );
10499           }
10500           if ( !bordElem )
10501             break;
10502
10503           // find the next bordElem
10504           const SMDS_MeshElement* nextBordElem = 0;
10505           for ( int iN = 0, nbN = bordElem->NbCornerNodes(); iN < nbN  && !nextBordElem; ++iN )
10506           {
10507             const SMDS_MeshNode* n = bordElem->GetNode( iN );
10508             if ( fissureNodes.count( n )) continue;
10509
10510             facetNodes[ sb._nbNodes ] = n;
10511             elemsByFacet.clear();
10512             if ( SMDS_Mesh::GetElementsByNodes( facetNodes, elemsByFacet ) > 1 )
10513             {
10514               for ( size_t iE = 0; iE < elemsByFacet.size(); ++iE )
10515                 if ( elemsByFacet[ iE ] != bordElem &&
10516                      !elemsByFacet[ iE ]->isMarked() )
10517                 {
10518                   theAffectedElems.insert( elemsByFacet[ iE ]);
10519                   elemsByFacet[ iE ]->setIsMarked( true );
10520                   if ( elemsByFacet[ iE ]->GetType() == bordElem->GetType() )
10521                     nextBordElem = elemsByFacet[ iE ];
10522                 }
10523             }
10524           }
10525           bordElem = nextBordElem;
10526
10527         } // while ( bordElem )
10528
10529         linkedBorders.clear(); // not to treat this link any more
10530
10531       } // loop on SubBorder's of a FissureBorder
10532
10533       bord->Clear();
10534
10535     } // loop on FissureBorder's
10536
10537
10538     // add elements sharing only one node of the fissure, except those sharing fissure edge nodes
10539
10540     // mark nodes of theAffectedElems
10541     SMESH_MeshAlgos::MarkElemNodes( theAffectedElems.begin(), theAffectedElems.end(), true );
10542
10543     // unmark nodes of the fissure
10544     elIt = theElemsOrNodes.begin();
10545     if ( (*elIt)->GetType() == SMDSAbs_Node )
10546       SMESH_MeshAlgos::MarkElems( elIt, theElemsOrNodes.end(), false );
10547     else
10548       SMESH_MeshAlgos::MarkElemNodes( elIt, theElemsOrNodes.end(), false );
10549
10550     std::vector< gp_XYZ > normVec;
10551
10552     // loop on nodes of the fissure, add elements having marked nodes
10553     for ( elIt = theElemsOrNodes.begin(); elIt != theElemsOrNodes.end(); ++elIt )
10554     {
10555       const SMDS_MeshElement* e = (*elIt);
10556       if ( e->GetType() != SMDSAbs_Node )
10557         e->setIsMarked( true ); // avoid adding a fissure element
10558
10559       for ( int iN = 0, nbN = e->NbCornerNodes(); iN < nbN; ++iN )
10560       {
10561         const SMDS_MeshNode* n = e->GetNode( iN );
10562         if ( fissEdgeNodes2Norm.count( n ))
10563           continue;
10564
10565         SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
10566         while ( invIt->more() )
10567         {
10568           const SMDS_MeshElement* eInv = invIt->next();
10569           if ( eInv->isMarked() ) continue;
10570           eInv->setIsMarked( true );
10571
10572           SMDS_ElemIteratorPtr nIt = eInv->nodesIterator();
10573           while( nIt->more() )
10574             if ( nIt->next()->isMarked())
10575             {
10576               theAffectedElems.insert( eInv );
10577               SMESH_MeshAlgos::MarkElems( eInv->nodesIterator(), true );
10578               n->setIsMarked( false );
10579               break;
10580             }
10581         }
10582       }
10583     }
10584
10585     // add elements on the fissure edge
10586     std::map< const SMDS_MeshNode*, FissureNormal >::iterator n2N;
10587     for ( n2N = fissEdgeNodes2Norm.begin(); n2N != fissEdgeNodes2Norm.end(); ++n2N )
10588     {
10589       const SMDS_MeshNode* edgeNode = n2N->first;
10590       const FissureNormal & normals = n2N->second;
10591
10592       SMDS_ElemIteratorPtr invIt = edgeNode->GetInverseElementIterator();
10593       while ( invIt->more() )
10594       {
10595         const SMDS_MeshElement* eInv = invIt->next();
10596         if ( eInv->isMarked() ) continue;
10597         eInv->setIsMarked( true );
10598
10599         // classify eInv using normals
10600         bool toAdd = normals.IsIn( edgeNode, eInv );
10601         if ( toAdd ) // check if all nodes lie on the fissure edge
10602         {
10603           bool notOnEdge = false;
10604           for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN  && !notOnEdge; ++iN )
10605             notOnEdge = !fissEdgeNodes2Norm.count( eInv->GetNode( iN ));
10606           toAdd = notOnEdge;
10607         }
10608         if ( toAdd )
10609         {
10610           theAffectedElems.insert( eInv );
10611         }
10612       }
10613     }
10614
10615     return;
10616   } // findAffectedElems()
10617 } // namespace
10618
10619 //================================================================================
10620 /*!
10621  * \brief Create elements equal (on same nodes) to given ones
10622  *  \param [in] theElements - a set of elems to duplicate. If it is empty, all
10623  *              elements of the uppest dimension are duplicated.
10624  */
10625 //================================================================================
10626
10627 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
10628 {
10629   ClearLastCreated();
10630   SMESHDS_Mesh* mesh = GetMeshDS();
10631
10632   // get an element type and an iterator over elements
10633
10634   SMDSAbs_ElementType type = SMDSAbs_All;
10635   SMDS_ElemIteratorPtr elemIt;
10636   if ( theElements.empty() )
10637   {
10638     if ( mesh->NbNodes() == 0 )
10639       return;
10640     // get most complex type
10641     SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10642       SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10643       SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10644     };
10645     for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10646       if ( mesh->GetMeshInfo().NbElements( types[i] ))
10647       {
10648         type = types[i];
10649         elemIt = mesh->elementsIterator( type );
10650         break;
10651       }
10652   }
10653   else
10654   {
10655     //type = (*theElements.begin())->GetType();
10656     elemIt = SMESHUtils::elemSetIterator( theElements );
10657   }
10658
10659   // un-mark all elements to avoid duplicating just created elements
10660   SMESH_MeshAlgos::MarkElems( mesh->elementsIterator( type ), false );
10661
10662   // duplicate elements
10663
10664   ElemFeatures elemType;
10665
10666   vector< const SMDS_MeshNode* > nodes;
10667   while ( elemIt->more() )
10668   {
10669     const SMDS_MeshElement* elem = elemIt->next();
10670     if (( type != SMDSAbs_All && elem->GetType() != type ) ||
10671         ( elem->isMarked() ))
10672       continue;
10673
10674     elemType.Init( elem, /*basicOnly=*/false );
10675     nodes.assign( elem->begin_nodes(), elem->end_nodes() );
10676
10677     if ( const SMDS_MeshElement* newElem = AddElement( nodes, elemType ))
10678       newElem->setIsMarked( true );
10679   }
10680 }
10681
10682 //================================================================================
10683 /*!
10684   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10685   \param theElems - the list of elements (edges or faces) to be replicated
10686   The nodes for duplication could be found from these elements
10687   \param theNodesNot - list of nodes to NOT replicate
10688   \param theAffectedElems - the list of elements (cells and edges) to which the
10689   replicated nodes should be associated to.
10690   \return TRUE if operation has been completed successfully, FALSE otherwise
10691 */
10692 //================================================================================
10693
10694 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10695                                     const TIDSortedElemSet& theNodesNot,
10696                                     const TIDSortedElemSet& theAffectedElems )
10697 {
10698   ClearLastCreated();
10699
10700   if ( theElems.size() == 0 )
10701     return false;
10702
10703   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10704   if ( !aMeshDS )
10705     return false;
10706
10707   bool res = false;
10708   TNodeNodeMap anOldNodeToNewNode;
10709   // duplicate elements and nodes
10710   res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10711   // replce nodes by duplications
10712   res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10713   return res;
10714 }
10715
10716 //================================================================================
10717 /*!
10718   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10719   \param theMeshDS - mesh instance
10720   \param theElems - the elements replicated or modified (nodes should be changed)
10721   \param theNodesNot - nodes to NOT replicate
10722   \param theNodeNodeMap - relation of old node to new created node
10723   \param theIsDoubleElem - flag os to replicate element or modify
10724   \return TRUE if operation has been completed successfully, FALSE otherwise
10725 */
10726 //================================================================================
10727
10728 bool SMESH_MeshEditor::doubleNodes(SMESHDS_Mesh*           theMeshDS,
10729                                    const TIDSortedElemSet& theElems,
10730                                    const TIDSortedElemSet& theNodesNot,
10731                                    TNodeNodeMap&           theNodeNodeMap,
10732                                    const bool              theIsDoubleElem )
10733 {
10734   // iterate through element and duplicate them (by nodes duplication)
10735   bool res = false;
10736   std::vector<const SMDS_MeshNode*> newNodes;
10737   ElemFeatures elemType;
10738
10739   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10740   for ( ;  elemItr != theElems.end(); ++elemItr )
10741   {
10742     const SMDS_MeshElement* anElem = *elemItr;
10743     // if (!anElem)
10744     //   continue;
10745
10746     // duplicate nodes to duplicate element
10747     bool isDuplicate = false;
10748     newNodes.resize( anElem->NbNodes() );
10749     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10750     int ind = 0;
10751     while ( anIter->more() )
10752     {
10753       const SMDS_MeshNode* aCurrNode = static_cast<const SMDS_MeshNode*>( anIter->next() );
10754       const SMDS_MeshNode*  aNewNode = aCurrNode;
10755       TNodeNodeMap::iterator     n2n = theNodeNodeMap.find( aCurrNode );
10756       if ( n2n != theNodeNodeMap.end() )
10757       {
10758         aNewNode = n2n->second;
10759       }
10760       else if ( theIsDoubleElem && !theNodesNot.count( aCurrNode ))
10761       {
10762         // duplicate node
10763         aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10764         copyPosition( aCurrNode, aNewNode );
10765         theNodeNodeMap[ aCurrNode ] = aNewNode;
10766         myLastCreatedNodes.push_back( aNewNode );
10767       }
10768       isDuplicate |= (aCurrNode != aNewNode);
10769       newNodes[ ind++ ] = aNewNode;
10770     }
10771     if ( !isDuplicate )
10772       continue;
10773
10774     if ( theIsDoubleElem )
10775       AddElement( newNodes, elemType.Init( anElem, /*basicOnly=*/false ));
10776     else
10777       theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], newNodes.size() );
10778
10779     res = true;
10780   }
10781   return res;
10782 }
10783
10784 //================================================================================
10785 /*!
10786   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10787   \param theNodes - identifiers of nodes to be doubled
10788   \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10789   nodes. If list of element identifiers is empty then nodes are doubled but
10790   they not assigned to elements
10791   \return TRUE if operation has been completed successfully, FALSE otherwise
10792 */
10793 //================================================================================
10794
10795 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10796                                     const std::list< int >& theListOfModifiedElems )
10797 {
10798   ClearLastCreated();
10799
10800   if ( theListOfNodes.size() == 0 )
10801     return false;
10802
10803   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10804   if ( !aMeshDS )
10805     return false;
10806
10807   // iterate through nodes and duplicate them
10808
10809   std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10810
10811   std::list< int >::const_iterator aNodeIter;
10812   for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10813   {
10814     const SMDS_MeshNode* aNode = aMeshDS->FindNode( *aNodeIter );
10815     if ( !aNode )
10816       continue;
10817
10818     // duplicate node
10819
10820     const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10821     if ( aNewNode )
10822     {
10823       copyPosition( aNode, aNewNode );
10824       anOldNodeToNewNode[ aNode ] = aNewNode;
10825       myLastCreatedNodes.push_back( aNewNode );
10826     }
10827   }
10828
10829   // Change nodes of elements
10830
10831   std::vector<const SMDS_MeshNode*> aNodeArr;
10832
10833   std::list< int >::const_iterator anElemIter;
10834   for ( anElemIter =  theListOfModifiedElems.begin();
10835         anElemIter != theListOfModifiedElems.end();
10836         anElemIter++ )
10837   {
10838     const SMDS_MeshElement* anElem = aMeshDS->FindElement( *anElemIter );
10839     if ( !anElem )
10840       continue;
10841
10842     aNodeArr.assign( anElem->begin_nodes(), anElem->end_nodes() );
10843     for( size_t i = 0; i < aNodeArr.size(); ++i )
10844     {
10845       std::map< const SMDS_MeshNode*, const SMDS_MeshNode* >::iterator n2n =
10846         anOldNodeToNewNode.find( aNodeArr[ i ]);
10847       if ( n2n != anOldNodeToNewNode.end() )
10848         aNodeArr[ i ] = n2n->second;
10849     }
10850     aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], aNodeArr.size() );
10851   }
10852
10853   return true;
10854 }
10855
10856 namespace {
10857
10858   //================================================================================
10859   /*!
10860     \brief Check if element located inside shape
10861     \return TRUE if IN or ON shape, FALSE otherwise
10862   */
10863   //================================================================================
10864
10865   template<class Classifier>
10866   bool isInside(const SMDS_MeshElement* theElem,
10867                 Classifier&             theClassifier,
10868                 const double            theTol)
10869   {
10870     gp_XYZ centerXYZ (0, 0, 0);
10871     for ( SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator(); aNodeItr->more(); )
10872       centerXYZ += SMESH_NodeXYZ( aNodeItr->next() );
10873
10874     gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10875     theClassifier.Perform(aPnt, theTol);
10876     TopAbs_State aState = theClassifier.State();
10877     return (aState == TopAbs_IN || aState == TopAbs_ON );
10878   }
10879
10880   //================================================================================
10881   /*!
10882    * \brief Classifier of the 3D point on the TopoDS_Face
10883    *        with interaface suitable for isInside()
10884    */
10885   //================================================================================
10886
10887   struct _FaceClassifier
10888   {
10889     Extrema_ExtPS       _extremum;
10890     BRepAdaptor_Surface _surface;
10891     TopAbs_State        _state;
10892
10893     _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10894     {
10895       _extremum.Initialize( _surface,
10896                             _surface.FirstUParameter(), _surface.LastUParameter(),
10897                             _surface.FirstVParameter(), _surface.LastVParameter(),
10898                             _surface.Tolerance(), _surface.Tolerance() );
10899     }
10900     void Perform(const gp_Pnt& aPnt, double theTol)
10901     {
10902       theTol *= theTol;
10903       _state = TopAbs_OUT;
10904       _extremum.Perform(aPnt);
10905       if ( _extremum.IsDone() )
10906         for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10907           _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10908     }
10909     TopAbs_State State() const
10910     {
10911       return _state;
10912     }
10913   };
10914 }
10915
10916 //================================================================================
10917 /*!
10918   \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
10919   This method is the first step of DoubleNodeElemGroupsInRegion.
10920   \param theElems - list of groups of elements (edges or faces) to be replicated
10921   \param theNodesNot - list of groups of nodes not to replicated
10922   \param theShape - shape to detect affected elements (element which geometric center
10923          located on or inside shape). If the shape is null, detection is done on faces orientations
10924          (select elements with a gravity center on the side given by faces normals).
10925          This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
10926          The replicated nodes should be associated to affected elements.
10927   \return true
10928   \sa DoubleNodeElemGroupsInRegion()
10929 */
10930 //================================================================================
10931
10932 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
10933                                                    const TIDSortedElemSet& theNodesNot,
10934                                                    const TopoDS_Shape&     theShape,
10935                                                    TIDSortedElemSet&       theAffectedElems)
10936 {
10937   if ( theShape.IsNull() )
10938   {
10939     findAffectedElems( theElems, theAffectedElems );
10940   }
10941   else
10942   {
10943     const double aTol = Precision::Confusion();
10944     std::unique_ptr< BRepClass3d_SolidClassifier> bsc3d;
10945     std::unique_ptr<_FaceClassifier>              aFaceClassifier;
10946     if ( theShape.ShapeType() == TopAbs_SOLID )
10947     {
10948       bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10949       bsc3d->PerformInfinitePoint(aTol);
10950     }
10951     else if (theShape.ShapeType() == TopAbs_FACE )
10952     {
10953       aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10954     }
10955
10956     // iterates on indicated elements and get elements by back references from their nodes
10957     TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10958     for ( ;  elemItr != theElems.end(); ++elemItr )
10959     {
10960       SMDS_MeshElement*     anElem = (SMDS_MeshElement*)*elemItr;
10961       SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10962       while ( nodeItr->more() )
10963       {
10964         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10965         if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10966           continue;
10967         SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10968         while ( backElemItr->more() )
10969         {
10970           const SMDS_MeshElement* curElem = backElemItr->next();
10971           if ( curElem && theElems.find(curElem) == theElems.end() &&
10972                ( bsc3d.get() ?
10973                  isInside( curElem, *bsc3d, aTol ) :
10974                  isInside( curElem, *aFaceClassifier, aTol )))
10975             theAffectedElems.insert( curElem );
10976         }
10977       }
10978     }
10979   }
10980   return true;
10981 }
10982
10983 //================================================================================
10984 /*!
10985   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10986   \param theElems - group of of elements (edges or faces) to be replicated
10987   \param theNodesNot - group of nodes not to replicate
10988   \param theShape - shape to detect affected elements (element which geometric center
10989   located on or inside shape).
10990   The replicated nodes should be associated to affected elements.
10991   \return TRUE if operation has been completed successfully, FALSE otherwise
10992 */
10993 //================================================================================
10994
10995 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
10996                                             const TIDSortedElemSet& theNodesNot,
10997                                             const TopoDS_Shape&     theShape )
10998 {
10999   if ( theShape.IsNull() )
11000     return false;
11001
11002   const double aTol = Precision::Confusion();
11003   SMESHUtils::Deleter< BRepClass3d_SolidClassifier> bsc3d;
11004   SMESHUtils::Deleter<_FaceClassifier>              aFaceClassifier;
11005   if ( theShape.ShapeType() == TopAbs_SOLID )
11006   {
11007     bsc3d._obj = new BRepClass3d_SolidClassifier( theShape );
11008     bsc3d->PerformInfinitePoint(aTol);
11009   }
11010   else if (theShape.ShapeType() == TopAbs_FACE )
11011   {
11012     aFaceClassifier._obj = new _FaceClassifier( TopoDS::Face( theShape ));
11013   }
11014
11015   // iterates on indicated elements and get elements by back references from their nodes
11016   TIDSortedElemSet anAffected;
11017   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11018   for ( ;  elemItr != theElems.end(); ++elemItr )
11019   {
11020     SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11021     if (!anElem)
11022       continue;
11023
11024     SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11025     while ( nodeItr->more() )
11026     {
11027       const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11028       if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11029         continue;
11030       SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11031       while ( backElemItr->more() )
11032       {
11033         const SMDS_MeshElement* curElem = backElemItr->next();
11034         if ( curElem && theElems.find(curElem) == theElems.end() &&
11035              ( bsc3d ?
11036                isInside( curElem, *bsc3d, aTol ) :
11037                isInside( curElem, *aFaceClassifier, aTol )))
11038           anAffected.insert( curElem );
11039       }
11040     }
11041   }
11042   return DoubleNodes( theElems, theNodesNot, anAffected );
11043 }
11044
11045 /*!
11046  *  \brief compute an oriented angle between two planes defined by four points.
11047  *  The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
11048  *  @param p0 base of the rotation axe
11049  *  @param p1 extremity of the rotation axe
11050  *  @param g1 belongs to the first plane
11051  *  @param g2 belongs to the second plane
11052  */
11053 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
11054 {
11055   gp_Vec vref(p0, p1);
11056   gp_Vec v1(p0, g1);
11057   gp_Vec v2(p0, g2);
11058   gp_Vec n1 = vref.Crossed(v1);
11059   gp_Vec n2 = vref.Crossed(v2);
11060   try {
11061     return n2.AngleWithRef(n1, vref);
11062   }
11063   catch ( Standard_Failure& ) {
11064   }
11065   return Max( v1.Magnitude(), v2.Magnitude() );
11066 }
11067
11068 /*!
11069  * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11070  *  The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
11071  * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
11072  * Triangles are transformed into prisms, and quadrangles into hexahedrons.
11073  * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
11074  * 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.
11075  * 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.
11076  * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
11077  * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
11078  * \param theElems - list of groups of volumes, where a group of volume is a set of
11079  *        SMDS_MeshElements sorted by Id.
11080  * \param createJointElems - if TRUE, create the elements
11081  * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
11082  *        the boundary between \a theDomains and the rest mesh
11083  * \return TRUE if operation has been completed successfully, FALSE otherwise
11084  */
11085 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11086                                                      bool                                 createJointElems,
11087                                                      bool                                 onAllBoundaries)
11088 {
11089   // MESSAGE("----------------------------------------------");
11090   // MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11091   // MESSAGE("----------------------------------------------");
11092
11093   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11094   meshDS->BuildDownWardConnectivity(true);
11095   CHRONO(50);
11096   SMDS_UnstructuredGrid *grid = meshDS->GetGrid();
11097
11098   // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11099   //     build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11100   //     build the list of nodes shared by 2 or more domains, with their domain indexes
11101
11102   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11103   std::map<int,int>celldom; // cell vtkId --> domain
11104   std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains;  // oldNode --> (id domain --> id cell)
11105   std::map<int, std::map<int,int> > nodeDomains; // oldId -->  (domainId --> newId)
11106   faceDomains.clear();
11107   celldom.clear();
11108   cellDomains.clear();
11109   nodeDomains.clear();
11110   std::map<int,int> emptyMap;
11111   std::set<int> emptySet;
11112   emptyMap.clear();
11113
11114   //MESSAGE(".. Number of domains :"<<theElems.size());
11115
11116   TIDSortedElemSet theRestDomElems;
11117   const int iRestDom  = -1;
11118   const int idom0     = onAllBoundaries ? iRestDom : 0;
11119   const int nbDomains = theElems.size();
11120
11121   // Check if the domains do not share an element
11122   for (int idom = 0; idom < nbDomains-1; idom++)
11123   {
11124     //       MESSAGE("... Check of domain #" << idom);
11125     const TIDSortedElemSet& domain = theElems[idom];
11126     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11127     for (; elemItr != domain.end(); ++elemItr)
11128     {
11129       const SMDS_MeshElement* anElem = *elemItr;
11130       int idombisdeb = idom + 1 ;
11131       // check if the element belongs to a domain further in the list
11132       for ( size_t idombis = idombisdeb; idombis < theElems.size(); idombis++ )
11133       {
11134         const TIDSortedElemSet& domainbis = theElems[idombis];
11135         if ( domainbis.count( anElem ))
11136         {
11137           MESSAGE(".... Domain #" << idom);
11138           MESSAGE(".... Domain #" << idombis);
11139           throw SALOME_Exception("The domains are not disjoint.");
11140           return false ;
11141         }
11142       }
11143     }
11144   }
11145
11146   for (int idom = 0; idom < nbDomains; idom++)
11147   {
11148
11149     // --- build a map (face to duplicate --> volume to modify)
11150     //     with all the faces shared by 2 domains (group of elements)
11151     //     and corresponding volume of this domain, for each shared face.
11152     //     a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11153
11154     //MESSAGE("... Neighbors of domain #" << idom);
11155     const TIDSortedElemSet& domain = theElems[idom];
11156     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11157     for (; elemItr != domain.end(); ++elemItr)
11158     {
11159       const SMDS_MeshElement* anElem = *elemItr;
11160       if (!anElem)
11161         continue;
11162       int vtkId = anElem->GetVtkID();
11163       //MESSAGE("  vtkId " << vtkId << " smdsId " << anElem->GetID());
11164       int neighborsVtkIds[NBMAXNEIGHBORS];
11165       int downIds[NBMAXNEIGHBORS];
11166       unsigned char downTypes[NBMAXNEIGHBORS];
11167       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11168       for (int n = 0; n < nbNeighbors; n++)
11169       {
11170         int smdsId = meshDS->FromVtkToSmds(neighborsVtkIds[n]);
11171         const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11172         if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
11173         {
11174           bool ok = false;
11175           for ( size_t idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
11176           {
11177             // MESSAGE("Domain " << idombis);
11178             const TIDSortedElemSet& domainbis = theElems[idombis];
11179             if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11180           }
11181           if ( ok || onAllBoundaries ) // the characteristics of the face is stored
11182           {
11183             DownIdType face(downIds[n], downTypes[n]);
11184             if (!faceDomains[face].count(idom))
11185             {
11186               faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11187               celldom[vtkId] = idom;
11188               //MESSAGE("       cell with a border " << vtkId << " domain " << idom);
11189             }
11190             if ( !ok )
11191             {
11192               theRestDomElems.insert( elem );
11193               faceDomains[face][iRestDom] = neighborsVtkIds[n];
11194               celldom[neighborsVtkIds[n]] = iRestDom;
11195             }
11196           }
11197         }
11198       }
11199     }
11200   }
11201
11202   //MESSAGE("Number of shared faces " << faceDomains.size());
11203   std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11204
11205   // --- explore the shared faces domain by domain,
11206   //     explore the nodes of the face and see if they belong to a cell in the domain,
11207   //     which has only a node or an edge on the border (not a shared face)
11208
11209   for (int idomain = idom0; idomain < nbDomains; idomain++)
11210   {
11211     //MESSAGE("Domain " << idomain);
11212     const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
11213     itface = faceDomains.begin();
11214     for (; itface != faceDomains.end(); ++itface)
11215     {
11216       const std::map<int, int>& domvol = itface->second;
11217       if (!domvol.count(idomain))
11218         continue;
11219       DownIdType face = itface->first;
11220       //MESSAGE(" --- face " << face.cellId);
11221       std::set<int> oldNodes;
11222       oldNodes.clear();
11223       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11224       std::set<int>::iterator itn = oldNodes.begin();
11225       for (; itn != oldNodes.end(); ++itn)
11226       {
11227         int oldId = *itn;
11228         //MESSAGE("     node " << oldId);
11229         vtkCellLinks::Link l = (static_cast <vtkCellLinks *>(grid->GetCellLinks()))->GetLink(oldId);
11230         for (int i=0; i<l.ncells; i++)
11231         {
11232           int vtkId = l.cells[i];
11233           const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->FromVtkToSmds(vtkId));
11234           if (!domain.count(anElem))
11235             continue;
11236           int vtkType = grid->GetCellType(vtkId);
11237           int downId = grid->CellIdToDownId(vtkId);
11238           if (downId < 0)
11239           {
11240             MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11241             continue; // not OK at this stage of the algorithm:
11242             //no cells created after BuildDownWardConnectivity
11243           }
11244           DownIdType aCell(downId, vtkType);
11245           cellDomains[aCell][idomain] = vtkId;
11246           celldom[vtkId] = idomain;
11247           //MESSAGE("       cell " << vtkId << " domain " << idomain);
11248         }
11249       }
11250     }
11251   }
11252
11253   // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11254   //     for each shared face, get the nodes
11255   //     for each node, for each domain of the face, create a clone of the node
11256
11257   // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11258   //     junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11259   //     the value is the ordered domain ids. (more than 4 domains not taken into account)
11260
11261   std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11262   std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11263   std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11264
11265   //MESSAGE(".. Duplication of the nodes");
11266   for (int idomain = idom0; idomain < nbDomains; idomain++)
11267   {
11268     itface = faceDomains.begin();
11269     for (; itface != faceDomains.end(); ++itface)
11270     {
11271       const std::map<int, int>& domvol = itface->second;
11272       if (!domvol.count(idomain))
11273         continue;
11274       DownIdType face = itface->first;
11275       //MESSAGE(" --- face " << face.cellId);
11276       std::set<int> oldNodes;
11277       oldNodes.clear();
11278       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11279       std::set<int>::iterator itn = oldNodes.begin();
11280       for (; itn != oldNodes.end(); ++itn)
11281       {
11282         int oldId = *itn;
11283         if (nodeDomains[oldId].empty())
11284         {
11285           nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11286           //MESSAGE("-+-+-b     oldNode " << oldId << " domain " << idomain);
11287         }
11288         std::map<int, int>::const_iterator itdom = domvol.begin();
11289         for (; itdom != domvol.end(); ++itdom)
11290         {
11291           int idom = itdom->first;
11292           //MESSAGE("         domain " << idom);
11293           if (!nodeDomains[oldId].count(idom)) // --- node to clone
11294           {
11295             if (nodeDomains[oldId].size() >= 2) // a multiple node
11296             {
11297               vector<int> orderedDoms;
11298               //MESSAGE("multiple node " << oldId);
11299               if (mutipleNodes.count(oldId))
11300                 orderedDoms = mutipleNodes[oldId];
11301               else
11302               {
11303                 map<int,int>::iterator it = nodeDomains[oldId].begin();
11304                 for (; it != nodeDomains[oldId].end(); ++it)
11305                   orderedDoms.push_back(it->first);
11306               }
11307               orderedDoms.push_back(idom); // TODO order ==> push_front or back
11308               //stringstream txt;
11309               //for (int i=0; i<orderedDoms.size(); i++)
11310               //  txt << orderedDoms[i] << " ";
11311               //MESSAGE("orderedDoms " << txt.str());
11312               mutipleNodes[oldId] = orderedDoms;
11313             }
11314             double *coords = grid->GetPoint(oldId);
11315             SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11316             copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11317             int newId = newNode->GetVtkID();
11318             nodeDomains[oldId][idom] = newId; // cloned node for other domains
11319             //MESSAGE("-+-+-c     oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11320           }
11321         }
11322       }
11323     }
11324   }
11325
11326   //MESSAGE(".. Creation of elements");
11327   for (int idomain = idom0; idomain < nbDomains; idomain++)
11328   {
11329     itface = faceDomains.begin();
11330     for (; itface != faceDomains.end(); ++itface)
11331     {
11332       std::map<int, int> domvol = itface->second;
11333       if (!domvol.count(idomain))
11334         continue;
11335       DownIdType face = itface->first;
11336       //MESSAGE(" --- face " << face.cellId);
11337       std::set<int> oldNodes;
11338       oldNodes.clear();
11339       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11340       int nbMultipleNodes = 0;
11341       std::set<int>::iterator itn = oldNodes.begin();
11342       for (; itn != oldNodes.end(); ++itn)
11343       {
11344         int oldId = *itn;
11345         if (mutipleNodes.count(oldId))
11346           nbMultipleNodes++;
11347       }
11348       if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11349       {
11350         //MESSAGE("multiple Nodes detected on a shared face");
11351         int downId = itface->first.cellId;
11352         unsigned char cellType = itface->first.cellType;
11353         // --- shared edge or shared face ?
11354         if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11355         {
11356           int nodes[3];
11357           int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11358           for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11359             if (mutipleNodes.count(nodes[i]))
11360               if (!mutipleNodesToFace.count(nodes[i]))
11361                 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11362         }
11363         else // shared face (between two volumes)
11364         {
11365           int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11366           const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11367           const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11368           for (int ie =0; ie < nbEdges; ie++)
11369           {
11370             int nodes[3];
11371             int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11372             if ( mutipleNodes.count(nodes[0]) && mutipleNodes.count( nodes[ nbNodes-1 ]))
11373             {
11374               vector<int> vn0 = mutipleNodes[nodes[0]];
11375               vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11376               vector<int> doms;
11377               for ( size_t i0 = 0; i0 < vn0.size(); i0++ )
11378                 for ( size_t i1 = 0; i1 < vn1.size(); i1++ )
11379                   if ( vn0[i0] == vn1[i1] )
11380                     doms.push_back( vn0[ i0 ]);
11381               if ( doms.size() > 2 )
11382               {
11383                 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11384                 double *coords = grid->GetPoint(nodes[0]);
11385                 gp_Pnt p0(coords[0], coords[1], coords[2]);
11386                 coords = grid->GetPoint(nodes[nbNodes - 1]);
11387                 gp_Pnt p1(coords[0], coords[1], coords[2]);
11388                 gp_Pnt gref;
11389                 int vtkVolIds[1000];  // an edge can belong to a lot of volumes
11390                 map<int, SMDS_MeshVolume*> domvol; // domain --> a volume with the edge
11391                 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11392                 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11393                 for ( size_t id = 0; id < doms.size(); id++ )
11394                 {
11395                   int idom = doms[id];
11396                   const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11397                   for ( int ivol = 0; ivol < nbvol; ivol++ )
11398                   {
11399                     int smdsId = meshDS->FromVtkToSmds(vtkVolIds[ivol]);
11400                     const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11401                     if (domain.count(elem))
11402                     {
11403                       const SMDS_MeshVolume* svol = SMDS_Mesh::DownCast<SMDS_MeshVolume>(elem);
11404                       domvol[idom] = (SMDS_MeshVolume*) svol;
11405                       //MESSAGE("  domain " << idom << " volume " << elem->GetID());
11406                       double values[3] = { 0,0,0 };
11407                       vtkIdType npts = 0;
11408                       vtkIdType const *pts(nullptr);
11409                       grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11410                       for ( vtkIdType i = 0; i < npts; ++i )
11411                       {
11412                         double *coords = grid->GetPoint( pts[i] );
11413                         for ( int j = 0; j < 3; ++j )
11414                           values[j] += coords[j] / npts;
11415                       }
11416                       if ( id == 0 )
11417                       {
11418                         gref.SetCoord( values[0], values[1], values[2] );
11419                         angleDom[idom] = 0;
11420                       }
11421                       else
11422                       {
11423                         gp_Pnt g( values[0], values[1], values[2] );
11424                         angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11425                         //MESSAGE("  angle=" << angleDom[idom]);
11426                       }
11427                       break;
11428                     }
11429                   }
11430                 }
11431                 map<double, int> sortedDom; // sort domains by angle
11432                 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11433                   sortedDom[ia->second] = ia->first;
11434                 vector<int> vnodes;
11435                 vector<int> vdom;
11436                 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11437                 {
11438                   vdom.push_back(ib->second);
11439                   //MESSAGE("  ordered domain " << ib->second << "  angle " << ib->first);
11440                 }
11441                 for (int ino = 0; ino < nbNodes; ino++)
11442                   vnodes.push_back(nodes[ino]);
11443                 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11444               }
11445             }
11446           }
11447         }
11448       }
11449     }
11450   }
11451
11452   // --- iterate on shared faces (volumes to modify, face to extrude)
11453   //     get node id's of the face (id SMDS = id VTK)
11454   //     create flat element with old and new nodes if requested
11455
11456   // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11457   //     (domain1 X domain2) = domain1 + MAXINT*domain2
11458
11459   std::map<int, std::map<long,int> > nodeQuadDomains;
11460   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11461
11462   //MESSAGE(".. Creation of elements: simple junction");
11463   if (createJointElems)
11464   {
11465     string joints2DName = "joints2D";
11466     mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str());
11467     SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11468     string joints3DName = "joints3D";
11469     mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str());
11470     SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11471
11472     itface = faceDomains.begin();
11473     for (; itface != faceDomains.end(); ++itface)
11474     {
11475       DownIdType face = itface->first;
11476       std::set<int> oldNodes;
11477       std::set<int>::iterator itn;
11478       oldNodes.clear();
11479       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11480
11481       std::map<int, int> domvol = itface->second;
11482       std::map<int, int>::iterator itdom = domvol.begin();
11483       int dom1 = itdom->first;
11484       int vtkVolId = itdom->second;
11485       itdom++;
11486       int dom2 = itdom->first;
11487       SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11488                                                        nodeQuadDomains);
11489       stringstream grpname;
11490       grpname << "j_";
11491       if (dom1 < dom2)
11492         grpname << dom1 << "_" << dom2;
11493       else
11494         grpname << dom2 << "_" << dom1;
11495       string namegrp = grpname.str();
11496       if (!mapOfJunctionGroups.count(namegrp))
11497         mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str());
11498       SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11499       if (sgrp)
11500         sgrp->Add(vol->GetID());
11501       if (vol->GetType() == SMDSAbs_Volume)
11502         joints3DGrp->Add(vol->GetID());
11503       else if (vol->GetType() == SMDSAbs_Face)
11504         joints2DGrp->Add(vol->GetID());
11505     }
11506   }
11507
11508   // --- create volumes on multiple domain intersection if requested
11509   //     iterate on mutipleNodesToFace
11510   //     iterate on edgesMultiDomains
11511
11512   //MESSAGE(".. Creation of elements: multiple junction");
11513   if (createJointElems)
11514   {
11515     // --- iterate on mutipleNodesToFace
11516
11517     std::map<int, std::vector<int> >::iterator itn =  mutipleNodesToFace.begin();
11518     for (; itn != mutipleNodesToFace.end(); ++itn)
11519     {
11520       int node = itn->first;
11521       vector<int> orderDom = itn->second;
11522       vector<vtkIdType> orderedNodes;
11523       for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11524         orderedNodes.push_back( nodeDomains[ node ][ orderDom[ idom ]]);
11525       SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11526
11527       stringstream grpname;
11528       grpname << "m2j_";
11529       grpname << 0 << "_" << 0;
11530       string namegrp = grpname.str();
11531       if (!mapOfJunctionGroups.count(namegrp))
11532         mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str());
11533       SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11534       if (sgrp)
11535         sgrp->Add(face->GetID());
11536     }
11537
11538     // --- iterate on edgesMultiDomains
11539
11540     std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11541     for (; ite != edgesMultiDomains.end(); ++ite)
11542     {
11543       vector<int> nodes = ite->first;
11544       vector<int> orderDom = ite->second;
11545       vector<vtkIdType> orderedNodes;
11546       if (nodes.size() == 2)
11547       {
11548         //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11549         for ( size_t ino = 0; ino < nodes.size(); ino++ )
11550           if ( orderDom.size() == 3 )
11551             for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11552               orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11553           else
11554             for (int idom = orderDom.size()-1; idom >=0; idom--)
11555               orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11556         SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11557
11558         string namegrp = "jointsMultiples";
11559         if (!mapOfJunctionGroups.count(namegrp))
11560           mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str());
11561         SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11562         if (sgrp)
11563           sgrp->Add(vol->GetID());
11564       }
11565       else
11566       {
11567         //INFOS("Quadratic multiple joints not implemented");
11568         // TODO quadratic nodes
11569       }
11570     }
11571   }
11572
11573   // --- list the explicit faces and edges of the mesh that need to be modified,
11574   //     i.e. faces and edges built with one or more duplicated nodes.
11575   //     associate these faces or edges to their corresponding domain.
11576   //     only the first domain found is kept when a face or edge is shared
11577
11578   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11579   std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11580   faceOrEdgeDom.clear();
11581   feDom.clear();
11582
11583   //MESSAGE(".. Modification of elements");
11584   for (int idomain = idom0; idomain < nbDomains; idomain++)
11585   {
11586     std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11587     for (; itnod != nodeDomains.end(); ++itnod)
11588     {
11589       int oldId = itnod->first;
11590       //MESSAGE("     node " << oldId);
11591       vtkCellLinks::Link l = (static_cast< vtkCellLinks *>(grid->GetCellLinks()))->GetLink(oldId);
11592       for (int i = 0; i < l.ncells; i++)
11593       {
11594         int vtkId = l.cells[i];
11595         int vtkType = grid->GetCellType(vtkId);
11596         int downId = grid->CellIdToDownId(vtkId);
11597         if (downId < 0)
11598           continue; // new cells: not to be modified
11599         DownIdType aCell(downId, vtkType);
11600         int volParents[1000];
11601         int nbvol = grid->GetParentVolumes(volParents, vtkId);
11602         for (int j = 0; j < nbvol; j++)
11603           if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11604             if (!feDom.count(vtkId))
11605             {
11606               feDom[vtkId] = idomain;
11607               faceOrEdgeDom[aCell] = emptyMap;
11608               faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11609               //MESSAGE("affect cell " << this->GetMeshDS()->FromVtkToSmds(vtkId) << " domain " << idomain
11610               //        << " type " << vtkType << " downId " << downId);
11611             }
11612       }
11613     }
11614   }
11615
11616   // --- iterate on shared faces (volumes to modify, face to extrude)
11617   //     get node id's of the face
11618   //     replace old nodes by new nodes in volumes, and update inverse connectivity
11619
11620   std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11621   for (int m=0; m<3; m++)
11622   {
11623     std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11624     itface = (*amap).begin();
11625     for (; itface != (*amap).end(); ++itface)
11626     {
11627       DownIdType face = itface->first;
11628       std::set<int> oldNodes;
11629       std::set<int>::iterator itn;
11630       oldNodes.clear();
11631       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11632       //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11633       std::map<int, int> localClonedNodeIds;
11634
11635       std::map<int, int> domvol = itface->second;
11636       std::map<int, int>::iterator itdom = domvol.begin();
11637       for (; itdom != domvol.end(); ++itdom)
11638       {
11639         int idom = itdom->first;
11640         int vtkVolId = itdom->second;
11641         //MESSAGE("modify nodes of cell " << this->GetMeshDS()->FromVtkToSmds(vtkVolId) << " domain " << idom);
11642         localClonedNodeIds.clear();
11643         for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11644         {
11645           int oldId = *itn;
11646           if (nodeDomains[oldId].count(idom))
11647           {
11648             localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11649             //MESSAGE("     node " << oldId << " --> " << localClonedNodeIds[oldId]);
11650           }
11651         }
11652         meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11653       }
11654     }
11655   }
11656
11657   // Remove empty groups (issue 0022812)
11658   std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
11659   for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
11660   {
11661     if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
11662       myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
11663   }
11664
11665   meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11666   grid->DeleteLinks();
11667
11668   CHRONOSTOP(50);
11669   counters::stats();
11670   return true;
11671 }
11672
11673 /*!
11674  * \brief Double nodes on some external faces and create flat elements.
11675  * Flat elements are mainly used by some types of mechanic calculations.
11676  *
11677  * Each group of the list must be constituted of faces.
11678  * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11679  * @param theElems - list of groups of faces, where a group of faces is a set of
11680  * SMDS_MeshElements sorted by Id.
11681  * @return TRUE if operation has been completed successfully, FALSE otherwise
11682  */
11683 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11684 {
11685   // MESSAGE("-------------------------------------------------");
11686   // MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11687   // MESSAGE("-------------------------------------------------");
11688
11689   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11690
11691   // --- For each group of faces
11692   //     duplicate the nodes, create a flat element based on the face
11693   //     replace the nodes of the faces by their clones
11694
11695   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11696   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11697   clonedNodes.clear();
11698   intermediateNodes.clear();
11699   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11700   mapOfJunctionGroups.clear();
11701
11702   for ( size_t idom = 0; idom < theElems.size(); idom++ )
11703   {
11704     const TIDSortedElemSet&           domain = theElems[idom];
11705     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11706     for ( ; elemItr != domain.end(); ++elemItr )
11707     {
11708       const SMDS_MeshFace* aFace = meshDS->DownCast<SMDS_MeshFace> ( *elemItr );
11709       if (!aFace)
11710         continue;
11711       // MESSAGE("aFace=" << aFace->GetID());
11712       bool isQuad = aFace->IsQuadratic();
11713       vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11714
11715       // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11716
11717       SMDS_NodeIteratorPtr nodeIt = aFace->nodeIterator();
11718       while (nodeIt->more())
11719       {
11720         const SMDS_MeshNode* node = nodeIt->next();
11721         bool isMedium = ( isQuad && aFace->IsMediumNode( node ));
11722         if (isMedium)
11723           ln2.push_back(node);
11724         else
11725           ln0.push_back(node);
11726
11727         const SMDS_MeshNode* clone = 0;
11728         if (!clonedNodes.count(node))
11729         {
11730           clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11731           copyPosition( node, clone );
11732           clonedNodes[node] = clone;
11733         }
11734         else
11735           clone = clonedNodes[node];
11736
11737         if (isMedium)
11738           ln3.push_back(clone);
11739         else
11740           ln1.push_back(clone);
11741
11742         const SMDS_MeshNode* inter = 0;
11743         if (isQuad && (!isMedium))
11744         {
11745           if (!intermediateNodes.count(node))
11746           {
11747             inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11748             copyPosition( node, inter );
11749             intermediateNodes[node] = inter;
11750           }
11751           else
11752             inter = intermediateNodes[node];
11753           ln4.push_back(inter);
11754         }
11755       }
11756
11757       // --- extrude the face
11758
11759       vector<const SMDS_MeshNode*> ln;
11760       SMDS_MeshVolume* vol = 0;
11761       vtkIdType aType = aFace->GetVtkType();
11762       switch (aType)
11763       {
11764       case VTK_TRIANGLE:
11765         vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11766         // MESSAGE("vol prism " << vol->GetID());
11767         ln.push_back(ln1[0]);
11768         ln.push_back(ln1[1]);
11769         ln.push_back(ln1[2]);
11770         break;
11771       case VTK_QUAD:
11772         vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11773         // MESSAGE("vol hexa " << vol->GetID());
11774         ln.push_back(ln1[0]);
11775         ln.push_back(ln1[1]);
11776         ln.push_back(ln1[2]);
11777         ln.push_back(ln1[3]);
11778         break;
11779       case VTK_QUADRATIC_TRIANGLE:
11780         vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11781                                 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11782         // MESSAGE("vol quad prism " << vol->GetID());
11783         ln.push_back(ln1[0]);
11784         ln.push_back(ln1[1]);
11785         ln.push_back(ln1[2]);
11786         ln.push_back(ln3[0]);
11787         ln.push_back(ln3[1]);
11788         ln.push_back(ln3[2]);
11789         break;
11790       case VTK_QUADRATIC_QUAD:
11791         //              vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11792         //                                      ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11793         //                                      ln4[0], ln4[1], ln4[2], ln4[3]);
11794         vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11795                                 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11796                                 ln4[0], ln4[1], ln4[2], ln4[3]);
11797         // MESSAGE("vol quad hexa " << vol->GetID());
11798         ln.push_back(ln1[0]);
11799         ln.push_back(ln1[1]);
11800         ln.push_back(ln1[2]);
11801         ln.push_back(ln1[3]);
11802         ln.push_back(ln3[0]);
11803         ln.push_back(ln3[1]);
11804         ln.push_back(ln3[2]);
11805         ln.push_back(ln3[3]);
11806         break;
11807       case VTK_POLYGON:
11808         break;
11809       default:
11810         break;
11811       }
11812
11813       if (vol)
11814       {
11815         stringstream grpname;
11816         grpname << "jf_";
11817         grpname << idom;
11818         string namegrp = grpname.str();
11819         if (!mapOfJunctionGroups.count(namegrp))
11820           mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str());
11821         SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11822         if (sgrp)
11823           sgrp->Add(vol->GetID());
11824       }
11825
11826       // --- modify the face
11827
11828       const_cast<SMDS_MeshFace*>( aFace )->ChangeNodes( &ln[0], ln.size() );
11829     }
11830   }
11831   return true;
11832 }
11833
11834 /*!
11835  *  \brief identify all the elements around a geom shape, get the faces delimiting the hole
11836  *  Build groups of volume to remove, groups of faces to replace on the skin of the object,
11837  *  groups of faces to remove inside the object, (idem edges).
11838  *  Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11839  */
11840 void SMESH_MeshEditor::CreateHoleSkin(double                          radius,
11841                                       const TopoDS_Shape&             theShape,
11842                                       SMESH_NodeSearcher*             theNodeSearcher,
11843                                       const char*                     groupName,
11844                                       std::vector<double>&            nodesCoords,
11845                                       std::vector<std::vector<int> >& listOfListOfNodes)
11846 {
11847   // MESSAGE("--------------------------------");
11848   // MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11849   // MESSAGE("--------------------------------");
11850
11851   // --- zone of volumes to remove is given :
11852   //     1 either by a geom shape (one or more vertices) and a radius,
11853   //     2 either by a group of nodes (representative of the shape)to use with the radius,
11854   //     3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11855   //     In the case 2, the group of nodes is an external group of nodes from another mesh,
11856   //     In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11857   //     defined by it's name.
11858
11859   SMESHDS_GroupBase* groupDS = 0;
11860   SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11861   while ( groupIt->more() )
11862   {
11863     groupDS = 0;
11864     SMESH_Group * group = groupIt->next();
11865     if ( !group ) continue;
11866     groupDS = group->GetGroupDS();
11867     if ( !groupDS || groupDS->IsEmpty() ) continue;
11868     std::string grpName = group->GetName();
11869     //MESSAGE("grpName=" << grpName);
11870     if (grpName == groupName)
11871       break;
11872     else
11873       groupDS = 0;
11874   }
11875
11876   bool isNodeGroup = false;
11877   bool isNodeCoords = false;
11878   if (groupDS)
11879   {
11880     if (groupDS->GetType() != SMDSAbs_Node)
11881       return;
11882     isNodeGroup = true;     // a group of nodes exists and it is in this mesh
11883   }
11884
11885   if (nodesCoords.size() > 0)
11886     isNodeCoords = true; // a list o nodes given by their coordinates
11887   //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11888
11889   // --- define groups to build
11890
11891   // --- group of SMDS volumes
11892   string grpvName = groupName;
11893   grpvName += "_vol";
11894   SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str());
11895   if (!grp)
11896   {
11897     MESSAGE("group not created " << grpvName);
11898     return;
11899   }
11900   SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
11901
11902   // --- group of SMDS faces on the skin
11903   string grpsName = groupName;
11904   grpsName += "_skin";
11905   SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str());
11906   if (!grps)
11907   {
11908     MESSAGE("group not created " << grpsName);
11909     return;
11910   }
11911   SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
11912
11913   // --- group of SMDS faces internal (several shapes)
11914   string grpiName = groupName;
11915   grpiName += "_internalFaces";
11916   SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str());
11917   if (!grpi)
11918   {
11919     MESSAGE("group not created " << grpiName);
11920     return;
11921   }
11922   SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
11923
11924   // --- group of SMDS faces internal (several shapes)
11925   string grpeiName = groupName;
11926   grpeiName += "_internalEdges";
11927   SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str());
11928   if (!grpei)
11929   {
11930     MESSAGE("group not created " << grpeiName);
11931     return;
11932   }
11933   SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
11934
11935   // --- build downward connectivity
11936
11937   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11938   meshDS->BuildDownWardConnectivity(true);
11939   SMDS_UnstructuredGrid* grid = meshDS->GetGrid();
11940
11941   // --- set of volumes detected inside
11942
11943   std::set<int> setOfInsideVol;
11944   std::set<int> setOfVolToCheck;
11945
11946   std::vector<gp_Pnt> gpnts;
11947   gpnts.clear();
11948
11949   if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
11950   {
11951     //MESSAGE("group of nodes provided");
11952     SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
11953     while ( elemIt->more() )
11954     {
11955       const SMDS_MeshElement* elem = elemIt->next();
11956       if (!elem)
11957         continue;
11958       const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
11959       if (!node)
11960         continue;
11961       SMDS_MeshElement* vol = 0;
11962       SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
11963       while (volItr->more())
11964       {
11965         vol = (SMDS_MeshElement*)volItr->next();
11966         setOfInsideVol.insert(vol->GetVtkID());
11967         sgrp->Add(vol->GetID());
11968       }
11969     }
11970   }
11971   else if (isNodeCoords)
11972   {
11973     //MESSAGE("list of nodes coordinates provided");
11974     size_t i = 0;
11975     int k = 0;
11976     while ( i < nodesCoords.size()-2 )
11977     {
11978       double x = nodesCoords[i++];
11979       double y = nodesCoords[i++];
11980       double z = nodesCoords[i++];
11981       gp_Pnt p = gp_Pnt(x, y ,z);
11982       gpnts.push_back(p);
11983       //MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
11984       k++;
11985     }
11986   }
11987   else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
11988   {
11989     //MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
11990     TopTools_IndexedMapOfShape vertexMap;
11991     TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
11992     gp_Pnt p = gp_Pnt(0,0,0);
11993     if (vertexMap.Extent() < 1)
11994       return;
11995
11996     for ( int i = 1; i <= vertexMap.Extent(); ++i )
11997     {
11998       const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
11999       p = BRep_Tool::Pnt(vertex);
12000       gpnts.push_back(p);
12001       //MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
12002     }
12003   }
12004
12005   if (gpnts.size() > 0)
12006   {
12007     const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
12008     //MESSAGE("startNode->nodeId " << nodeId);
12009
12010     double radius2 = radius*radius;
12011     //MESSAGE("radius2 " << radius2);
12012
12013     // --- volumes on start node
12014
12015     setOfVolToCheck.clear();
12016     SMDS_MeshElement* startVol = 0;
12017     SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
12018     while (volItr->more())
12019     {
12020       startVol = (SMDS_MeshElement*)volItr->next();
12021       setOfVolToCheck.insert(startVol->GetVtkID());
12022     }
12023     if (setOfVolToCheck.empty())
12024     {
12025       MESSAGE("No volumes found");
12026       return;
12027     }
12028
12029     // --- starting with central volumes then their neighbors, check if they are inside
12030     //     or outside the domain, until no more new neighbor volume is inside.
12031     //     Fill the group of inside volumes
12032
12033     std::map<int, double> mapOfNodeDistance2;
12034     mapOfNodeDistance2.clear();
12035     std::set<int> setOfOutsideVol;
12036     while (!setOfVolToCheck.empty())
12037     {
12038       std::set<int>::iterator it = setOfVolToCheck.begin();
12039       int vtkId = *it;
12040       //MESSAGE("volume to check,  vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12041       bool volInside = false;
12042       vtkIdType npts = 0;
12043       vtkIdType const *pts(nullptr);
12044       grid->GetCellPoints(vtkId, npts, pts);
12045       for (int i=0; i<npts; i++)
12046       {
12047         double distance2 = 0;
12048         if (mapOfNodeDistance2.count(pts[i]))
12049         {
12050           distance2 = mapOfNodeDistance2[pts[i]];
12051           //MESSAGE("point " << pts[i] << " distance2 " << distance2);
12052         }
12053         else
12054         {
12055           double *coords = grid->GetPoint(pts[i]);
12056           gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
12057           distance2 = 1.E40;
12058           for ( size_t j = 0; j < gpnts.size(); j++ )
12059           {
12060             double d2 = aPoint.SquareDistance( gpnts[ j ]);
12061             if (d2 < distance2)
12062             {
12063               distance2 = d2;
12064               if (distance2 < radius2)
12065                 break;
12066             }
12067           }
12068           mapOfNodeDistance2[pts[i]] = distance2;
12069           //MESSAGE("  point "  << pts[i]  << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " <<  coords[2]);
12070         }
12071         if (distance2 < radius2)
12072         {
12073           volInside = true; // one or more nodes inside the domain
12074           sgrp->Add(meshDS->FromVtkToSmds(vtkId));
12075           break;
12076         }
12077       }
12078       if (volInside)
12079       {
12080         setOfInsideVol.insert(vtkId);
12081         //MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12082         int neighborsVtkIds[NBMAXNEIGHBORS];
12083         int downIds[NBMAXNEIGHBORS];
12084         unsigned char downTypes[NBMAXNEIGHBORS];
12085         int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12086         for (int n = 0; n < nbNeighbors; n++)
12087           if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12088             setOfVolToCheck.insert(neighborsVtkIds[n]);
12089       }
12090       else
12091       {
12092         setOfOutsideVol.insert(vtkId);
12093         //MESSAGE("  volume outside, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12094       }
12095       setOfVolToCheck.erase(vtkId);
12096     }
12097   }
12098
12099   // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12100   //     If yes, add the volume to the inside set
12101
12102   bool addedInside = true;
12103   std::set<int> setOfVolToReCheck;
12104   while (addedInside)
12105   {
12106     //MESSAGE(" --------------------------- re check");
12107     addedInside = false;
12108     std::set<int>::iterator itv = setOfInsideVol.begin();
12109     for (; itv != setOfInsideVol.end(); ++itv)
12110     {
12111       int vtkId = *itv;
12112       int neighborsVtkIds[NBMAXNEIGHBORS];
12113       int downIds[NBMAXNEIGHBORS];
12114       unsigned char downTypes[NBMAXNEIGHBORS];
12115       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12116       for (int n = 0; n < nbNeighbors; n++)
12117         if (!setOfInsideVol.count(neighborsVtkIds[n]))
12118           setOfVolToReCheck.insert(neighborsVtkIds[n]);
12119     }
12120     setOfVolToCheck = setOfVolToReCheck;
12121     setOfVolToReCheck.clear();
12122     while  (!setOfVolToCheck.empty())
12123     {
12124       std::set<int>::iterator it = setOfVolToCheck.begin();
12125       int vtkId = *it;
12126       if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12127       {
12128         //MESSAGE("volume to recheck,  vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12129         int countInside = 0;
12130         int neighborsVtkIds[NBMAXNEIGHBORS];
12131         int downIds[NBMAXNEIGHBORS];
12132         unsigned char downTypes[NBMAXNEIGHBORS];
12133         int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12134         for (int n = 0; n < nbNeighbors; n++)
12135           if (setOfInsideVol.count(neighborsVtkIds[n]))
12136             countInside++;
12137         //MESSAGE("countInside " << countInside);
12138         if (countInside > 1)
12139         {
12140           //MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12141           setOfInsideVol.insert(vtkId);
12142           sgrp->Add(meshDS->FromVtkToSmds(vtkId));
12143           addedInside = true;
12144         }
12145         else
12146           setOfVolToReCheck.insert(vtkId);
12147       }
12148       setOfVolToCheck.erase(vtkId);
12149     }
12150   }
12151
12152   // --- map of Downward faces at the boundary, inside the global volume
12153   //     map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12154   //     fill group of SMDS faces inside the volume (when several volume shapes)
12155   //     fill group of SMDS faces on the skin of the global volume (if skin)
12156
12157   std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12158   std::map<DownIdType, int, DownIdCompare> skinFaces;     // faces on the skin of the global volume --> corresponding cell
12159   std::set<int>::iterator it = setOfInsideVol.begin();
12160   for (; it != setOfInsideVol.end(); ++it)
12161   {
12162     int vtkId = *it;
12163     //MESSAGE("  vtkId " << vtkId  << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12164     int neighborsVtkIds[NBMAXNEIGHBORS];
12165     int downIds[NBMAXNEIGHBORS];
12166     unsigned char downTypes[NBMAXNEIGHBORS];
12167     int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12168     for (int n = 0; n < nbNeighbors; n++)
12169     {
12170       int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12171       if (neighborDim == 3)
12172       {
12173         if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12174         {
12175           DownIdType face(downIds[n], downTypes[n]);
12176           boundaryFaces[face] = vtkId;
12177         }
12178         // if the face between to volumes is in the mesh, get it (internal face between shapes)
12179         int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12180         if (vtkFaceId >= 0)
12181         {
12182           sgrpi->Add(meshDS->FromVtkToSmds(vtkFaceId));
12183           // find also the smds edges on this face
12184           int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12185           const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12186           const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12187           for (int i = 0; i < nbEdges; i++)
12188           {
12189             int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12190             if (vtkEdgeId >= 0)
12191               sgrpei->Add(meshDS->FromVtkToSmds(vtkEdgeId));
12192           }
12193         }
12194       }
12195       else if (neighborDim == 2) // skin of the volume
12196       {
12197         DownIdType face(downIds[n], downTypes[n]);
12198         skinFaces[face] = vtkId;
12199         int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12200         if (vtkFaceId >= 0)
12201           sgrps->Add(meshDS->FromVtkToSmds(vtkFaceId));
12202       }
12203     }
12204   }
12205
12206   // --- identify the edges constituting the wire of each subshape on the skin
12207   //     define polylines with the nodes of edges, equivalent to wires
12208   //     project polylines on subshapes, and partition, to get geom faces
12209
12210   std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12211   std::set<int> emptySet;
12212   emptySet.clear();
12213   std::set<int> shapeIds;
12214
12215   SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12216   while (itelem->more())
12217   {
12218     const SMDS_MeshElement *elem = itelem->next();
12219     int shapeId = elem->getshapeId();
12220     int   vtkId = elem->GetVtkID();
12221     if (!shapeIdToVtkIdSet.count(shapeId))
12222     {
12223       shapeIdToVtkIdSet[shapeId] = emptySet;
12224       shapeIds.insert(shapeId);
12225     }
12226     shapeIdToVtkIdSet[shapeId].insert(vtkId);
12227   }
12228
12229   std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12230   std::set<DownIdType, DownIdCompare> emptyEdges;
12231   emptyEdges.clear();
12232
12233   std::map<int, std::set<int> >::iterator itShape =  shapeIdToVtkIdSet.begin();
12234   for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12235   {
12236     int shapeId = itShape->first;
12237     //MESSAGE(" --- Shape ID --- "<< shapeId);
12238     shapeIdToEdges[shapeId] = emptyEdges;
12239
12240     std::vector<int> nodesEdges;
12241
12242     std::set<int>::iterator its = itShape->second.begin();
12243     for (; its != itShape->second.end(); ++its)
12244     {
12245       int vtkId = *its;
12246       //MESSAGE("     " << vtkId);
12247       int neighborsVtkIds[NBMAXNEIGHBORS];
12248       int downIds[NBMAXNEIGHBORS];
12249       unsigned char downTypes[NBMAXNEIGHBORS];
12250       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12251       for (int n = 0; n < nbNeighbors; n++)
12252       {
12253         if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12254           continue;
12255         int smdsId = meshDS->FromVtkToSmds(neighborsVtkIds[n]);
12256         const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12257         if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12258         {
12259           DownIdType edge(downIds[n], downTypes[n]);
12260           if (!shapeIdToEdges[shapeId].count(edge))
12261           {
12262             shapeIdToEdges[shapeId].insert(edge);
12263             int vtkNodeId[3];
12264             int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12265             nodesEdges.push_back(vtkNodeId[0]);
12266             nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12267             //MESSAGE("       --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12268           }
12269         }
12270       }
12271     }
12272
12273     std::list<int> order;
12274     order.clear();
12275     if (nodesEdges.size() > 0)
12276     {
12277       order.push_back(nodesEdges[0]); //MESSAGE("       --- back " << order.back()+1); // SMDS id = VTK id + 1;
12278       nodesEdges[0] = -1;
12279       order.push_back(nodesEdges[1]); //MESSAGE("       --- back " << order.back()+1);
12280       nodesEdges[1] = -1; // do not reuse this edge
12281       bool found = true;
12282       while (found)
12283       {
12284         int nodeTofind = order.back(); // try first to push back
12285         int i = 0;
12286         for ( i = 0; i < (int)nodesEdges.size(); i++ )
12287           if (nodesEdges[i] == nodeTofind)
12288             break;
12289         if ( i == (int) nodesEdges.size() )
12290           found = false; // no follower found on back
12291         else
12292         {
12293           if (i%2) // odd ==> use the previous one
12294             if (nodesEdges[i-1] < 0)
12295               found = false;
12296             else
12297             {
12298               order.push_back(nodesEdges[i-1]); //MESSAGE("       --- back " << order.back()+1);
12299               nodesEdges[i-1] = -1;
12300             }
12301           else // even ==> use the next one
12302             if (nodesEdges[i+1] < 0)
12303               found = false;
12304             else
12305             {
12306               order.push_back(nodesEdges[i+1]); //MESSAGE("       --- back " << order.back()+1);
12307               nodesEdges[i+1] = -1;
12308             }
12309         }
12310         if (found)
12311           continue;
12312         // try to push front
12313         found = true;
12314         nodeTofind = order.front(); // try to push front
12315         for ( i = 0;  i < (int)nodesEdges.size(); i++ )
12316           if ( nodesEdges[i] == nodeTofind )
12317             break;
12318         if ( i == (int)nodesEdges.size() )
12319         {
12320           found = false; // no predecessor found on front
12321           continue;
12322         }
12323         if (i%2) // odd ==> use the previous one
12324           if (nodesEdges[i-1] < 0)
12325             found = false;
12326           else
12327           {
12328             order.push_front(nodesEdges[i-1]); //MESSAGE("       --- front " << order.front()+1);
12329             nodesEdges[i-1] = -1;
12330           }
12331         else // even ==> use the next one
12332           if (nodesEdges[i+1] < 0)
12333             found = false;
12334           else
12335           {
12336             order.push_front(nodesEdges[i+1]); //MESSAGE("       --- front " << order.front()+1);
12337             nodesEdges[i+1] = -1;
12338           }
12339       }
12340     }
12341
12342
12343     std::vector<int> nodes;
12344     nodes.push_back(shapeId);
12345     std::list<int>::iterator itl = order.begin();
12346     for (; itl != order.end(); itl++)
12347     {
12348       nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12349       //MESSAGE("              ordered node " << nodes[nodes.size()-1]);
12350     }
12351     listOfListOfNodes.push_back(nodes);
12352   }
12353
12354   //     partition geom faces with blocFissure
12355   //     mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12356   //     mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12357
12358   return;
12359 }
12360
12361
12362 //================================================================================
12363 /*!
12364  * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12365  * The created 2D mesh elements based on nodes of free faces of boundary volumes
12366  * \return TRUE if operation has been completed successfully, FALSE otherwise
12367  */
12368 //================================================================================
12369
12370 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12371 {
12372   // iterates on volume elements and detect all free faces on them
12373   SMESHDS_Mesh* aMesh = GetMeshDS();
12374   if (!aMesh)
12375     return false;
12376
12377   ElemFeatures faceType( SMDSAbs_Face );
12378   int nbFree = 0, nbExisted = 0, nbCreated = 0;
12379   SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12380   while(vIt->more())
12381   {
12382     const SMDS_MeshVolume* volume = vIt->next();
12383     SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12384     vTool.SetExternalNormal();
12385     const int iQuad = volume->IsQuadratic();
12386     faceType.SetQuad( iQuad );
12387     for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12388     {
12389       if (!vTool.IsFreeFace(iface))
12390         continue;
12391       nbFree++;
12392       vector<const SMDS_MeshNode *> nodes;
12393       int nbFaceNodes = vTool.NbFaceNodes(iface);
12394       const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12395       int inode = 0;
12396       for ( ; inode < nbFaceNodes; inode += iQuad+1)
12397         nodes.push_back(faceNodes[inode]);
12398
12399       if (iQuad) // add medium nodes
12400       {
12401         for ( inode = 1; inode < nbFaceNodes; inode += 2)
12402           nodes.push_back(faceNodes[inode]);
12403         if ( nbFaceNodes == 9 ) // bi-quadratic quad
12404           nodes.push_back(faceNodes[8]);
12405       }
12406       // add new face based on volume nodes
12407       if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) )
12408       {
12409         nbExisted++; // face already exists
12410       }
12411       else
12412       {
12413         AddElement( nodes, faceType.SetPoly( nbFaceNodes/(iQuad+1) > 4 ));
12414         nbCreated++;
12415       }
12416     }
12417   }
12418   return ( nbFree == ( nbExisted + nbCreated ));
12419 }
12420
12421 namespace
12422 {
12423   inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12424   {
12425     if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12426       return n;
12427     return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12428   }
12429 }
12430 //================================================================================
12431 /*!
12432  * \brief Creates missing boundary elements
12433  *  \param elements - elements whose boundary is to be checked
12434  *  \param dimension - defines type of boundary elements to create
12435  *  \param group - a group to store created boundary elements in
12436  *  \param targetMesh - a mesh to store created boundary elements in
12437  *  \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12438  *  \param toCopyExistingBoundary - if true, not only new but also pre-existing
12439  *                                boundary elements will be copied into the targetMesh
12440  *  \param toAddExistingBondary - if true, not only new but also pre-existing
12441  *                                boundary elements will be added into the new group
12442  *  \param aroundElements - if true, elements will be created on boundary of given
12443  *                          elements else, on boundary of the whole mesh.
12444  * \return nb of added boundary elements
12445  */
12446 //================================================================================
12447
12448 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12449                                        Bnd_Dimension           dimension,
12450                                        SMESH_Group*            group/*=0*/,
12451                                        SMESH_Mesh*             targetMesh/*=0*/,
12452                                        bool                    toCopyElements/*=false*/,
12453                                        bool                    toCopyExistingBoundary/*=false*/,
12454                                        bool                    toAddExistingBondary/*= false*/,
12455                                        bool                    aroundElements/*= false*/)
12456 {
12457   SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12458   SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12459   // hope that all elements are of the same type, do not check them all
12460   if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12461     throw SALOME_Exception(LOCALIZED("wrong element type"));
12462
12463   if ( !targetMesh )
12464     toCopyElements = toCopyExistingBoundary = false;
12465
12466   SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12467   SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12468   int nbAddedBnd = 0;
12469
12470   // editor adding present bnd elements and optionally holding elements to add to the group
12471   SMESH_MeshEditor* presentEditor;
12472   SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12473   presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12474
12475   SMESH_MesherHelper helper( *myMesh );
12476   const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12477   SMDS_VolumeTool vTool;
12478   TIDSortedElemSet avoidSet;
12479   const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12480   size_t inode;
12481
12482   typedef vector<const SMDS_MeshNode*> TConnectivity;
12483   TConnectivity tgtNodes;
12484   ElemFeatures elemKind( missType ), elemToCopy;
12485
12486   vector<const SMDS_MeshElement*> presentBndElems;
12487   vector<TConnectivity>           missingBndElems;
12488   vector<int>                     freeFacets;
12489   TConnectivity nodes, elemNodes;
12490
12491   SMDS_ElemIteratorPtr eIt;
12492   if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12493   else                  eIt = SMESHUtils::elemSetIterator( elements );
12494
12495   while ( eIt->more() )
12496   {
12497     const SMDS_MeshElement* elem = eIt->next();
12498     const int              iQuad = elem->IsQuadratic();
12499     elemKind.SetQuad( iQuad );
12500
12501     // ------------------------------------------------------------------------------------
12502     // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12503     // ------------------------------------------------------------------------------------
12504     presentBndElems.clear();
12505     missingBndElems.clear();
12506     freeFacets.clear(); nodes.clear(); elemNodes.clear();
12507     if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12508     {
12509       const SMDS_MeshElement* otherVol = 0;
12510       for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12511       {
12512         if ( !vTool.IsFreeFace(iface, &otherVol) &&
12513              ( !aroundElements || elements.count( otherVol )))
12514           continue;
12515         freeFacets.push_back( iface );
12516       }
12517       if ( missType == SMDSAbs_Face )
12518         vTool.SetExternalNormal();
12519       for ( size_t i = 0; i < freeFacets.size(); ++i )
12520       {
12521         int                iface = freeFacets[i];
12522         const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12523         const size_t nbFaceNodes = vTool.NbFaceNodes (iface);
12524         if ( missType == SMDSAbs_Edge ) // boundary edges
12525         {
12526           nodes.resize( 2+iQuad );
12527           for ( size_t i = 0; i < nbFaceNodes; i += 1+iQuad )
12528           {
12529             for ( size_t j = 0; j < nodes.size(); ++j )
12530               nodes[ j ] = nn[ i+j ];
12531             if ( const SMDS_MeshElement* edge =
12532                  aMesh->FindElement( nodes, SMDSAbs_Edge, /*noMedium=*/false ))
12533               presentBndElems.push_back( edge );
12534             else
12535               missingBndElems.push_back( nodes );
12536           }
12537         }
12538         else // boundary face
12539         {
12540           nodes.clear();
12541           for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12542             nodes.push_back( nn[inode] ); // add corner nodes
12543           if (iQuad)
12544             for ( inode = 1; inode < nbFaceNodes; inode += 2)
12545               nodes.push_back( nn[inode] ); // add medium nodes
12546           int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12547           if ( iCenter > 0 )
12548             nodes.push_back( vTool.GetNodes()[ iCenter ] );
12549
12550           if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12551                                                                SMDSAbs_Face, /*noMedium=*/false ))
12552             presentBndElems.push_back( f );
12553           else
12554             missingBndElems.push_back( nodes );
12555
12556           if ( targetMesh != myMesh )
12557           {
12558             // add 1D elements on face boundary to be added to a new mesh
12559             const SMDS_MeshElement* edge;
12560             for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12561             {
12562               if ( iQuad )
12563                 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12564               else
12565                 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12566               if ( edge && avoidSet.insert( edge ).second )
12567                 presentBndElems.push_back( edge );
12568             }
12569           }
12570         }
12571       }
12572     }
12573     else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12574     {
12575       avoidSet.clear(), avoidSet.insert( elem );
12576       elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesIterator() ),
12577                         SMDS_MeshElement::iterator() );
12578       elemNodes.push_back( elemNodes[0] );
12579       nodes.resize( 2 + iQuad );
12580       const int nbLinks = elem->NbCornerNodes();
12581       for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12582       {
12583         nodes[0] = elemNodes[iN];
12584         nodes[1] = elemNodes[iN+1+iQuad];
12585         if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12586           continue; // not free link
12587
12588         if ( iQuad ) nodes[2] = elemNodes[iN+1];
12589         if ( const SMDS_MeshElement* edge =
12590              aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12591           presentBndElems.push_back( edge );
12592         else
12593           missingBndElems.push_back( nodes );
12594       }
12595     }
12596
12597     // ---------------------------------
12598     // 2. Add missing boundary elements
12599     // ---------------------------------
12600     if ( targetMesh != myMesh )
12601       // instead of making a map of nodes in this mesh and targetMesh,
12602       // we create nodes with same IDs.
12603       for ( size_t i = 0; i < missingBndElems.size(); ++i )
12604       {
12605         TConnectivity& srcNodes = missingBndElems[i];
12606         tgtNodes.resize( srcNodes.size() );
12607         for ( inode = 0; inode < srcNodes.size(); ++inode )
12608           tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12609         if ( /*aroundElements && */tgtEditor.GetMeshDS()->FindElement( tgtNodes,
12610                                                                        missType,
12611                                                                        /*noMedium=*/false))
12612           continue;
12613         tgtEditor.AddElement( tgtNodes, elemKind.SetPoly( tgtNodes.size()/(iQuad+1) > 4 ));
12614         ++nbAddedBnd;
12615       }
12616     else
12617       for ( size_t i = 0; i < missingBndElems.size(); ++i )
12618       {
12619         TConnectivity& nodes = missingBndElems[ i ];
12620         if ( /*aroundElements && */tgtEditor.GetMeshDS()->FindElement( nodes,
12621                                                                        missType,
12622                                                                        /*noMedium=*/false))
12623           continue;
12624         SMDS_MeshElement* newElem =
12625           tgtEditor.AddElement( nodes, elemKind.SetPoly( nodes.size()/(iQuad+1) > 4 ));
12626         nbAddedBnd += bool( newElem );
12627
12628         // try to set a new element to a shape
12629         if ( myMesh->HasShapeToMesh() )
12630         {
12631           bool ok = true;
12632           set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12633           const size_t nbN = nodes.size() / (iQuad+1 );
12634           for ( inode = 0; inode < nbN && ok; ++inode )
12635           {
12636             pair<int, TopAbs_ShapeEnum> i_stype =
12637               helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12638             if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12639               mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12640           }
12641           if ( ok && mediumShapes.size() > 1 )
12642           {
12643             set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12644             pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12645             for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12646             {
12647               if (( ok = ( stype_i->first != stype_i_0.first )))
12648                 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12649                                         aMesh->IndexToShape( stype_i_0.second ));
12650             }
12651           }
12652           if ( ok && mediumShapes.begin()->first == missShapeType )
12653             aMesh->SetMeshElementOnShape( newElem, mediumShapes.begin()->second );
12654         }
12655       }
12656
12657     // ----------------------------------
12658     // 3. Copy present boundary elements
12659     // ----------------------------------
12660     if ( toCopyExistingBoundary )
12661       for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12662       {
12663         const SMDS_MeshElement* e = presentBndElems[i];
12664         tgtNodes.resize( e->NbNodes() );
12665         for ( inode = 0; inode < tgtNodes.size(); ++inode )
12666           tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12667         presentEditor->AddElement( tgtNodes, elemToCopy.Init( e ));
12668       }
12669     else // store present elements to add them to a group
12670       for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12671       {
12672         presentEditor->myLastCreatedElems.push_back( presentBndElems[ i ]);
12673       }
12674
12675   } // loop on given elements
12676
12677   // ---------------------------------------------
12678   // 4. Fill group with boundary elements
12679   // ---------------------------------------------
12680   if ( group )
12681   {
12682     if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12683       for ( size_t i = 0; i < tgtEditor.myLastCreatedElems.size(); ++i )
12684         g->SMDSGroup().Add( tgtEditor.myLastCreatedElems[ i ]);
12685   }
12686   tgtEditor.myLastCreatedElems.clear();
12687   tgtEditor2.myLastCreatedElems.clear();
12688
12689   // -----------------------
12690   // 5. Copy given elements
12691   // -----------------------
12692   if ( toCopyElements && targetMesh != myMesh )
12693   {
12694     if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12695     else                  eIt = SMESHUtils::elemSetIterator( elements );
12696     while (eIt->more())
12697     {
12698       const SMDS_MeshElement* elem = eIt->next();
12699       tgtNodes.resize( elem->NbNodes() );
12700       for ( inode = 0; inode < tgtNodes.size(); ++inode )
12701         tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12702       tgtEditor.AddElement( tgtNodes, elemToCopy.Init( elem ));
12703
12704       tgtEditor.myLastCreatedElems.clear();
12705     }
12706   }
12707   return nbAddedBnd;
12708 }
12709
12710 //================================================================================
12711 /*!
12712  * \brief Copy node position and set \a to node on the same geometry
12713  */
12714 //================================================================================
12715
12716 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
12717                                      const SMDS_MeshNode* to )
12718 {
12719   if ( !from || !to ) return;
12720
12721   SMDS_PositionPtr pos = from->GetPosition();
12722   if ( !pos || from->getshapeId() < 1 ) return;
12723
12724   switch ( pos->GetTypeOfPosition() )
12725   {
12726   case SMDS_TOP_3DSPACE: break;
12727
12728   case SMDS_TOP_FACE:
12729   {
12730     SMDS_FacePositionPtr fPos = pos;
12731     GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
12732                                 fPos->GetUParameter(), fPos->GetVParameter() );
12733     break;
12734   }
12735   case SMDS_TOP_EDGE:
12736   {
12737     // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
12738     SMDS_EdgePositionPtr ePos = pos;
12739     GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
12740     break;
12741   }
12742   case SMDS_TOP_VERTEX:
12743   {
12744     GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
12745     break;
12746   }
12747   case SMDS_TOP_UNSPEC:
12748   default:;
12749   }
12750 }