Salome HOME
bos #24085 fix TSplitMethod method for Windows (to avoid crash)
[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     // order of facets in HEX according to SMDS_VolumeTool::Hexa_F :
2073     // B, T, L, B, R, F
2074     const int iF = ( facetToSplit < 2 ) ? 0 : 1 + ( facetToSplit-2 ) % 2; // [0,1,2]
2075
2076     if ( methodFlags == SMESH_MeshEditor::HEXA_TO_4_PRISMS )
2077     {
2078       static TSplitMethod to4methods[4]; // order BT, LR, FB
2079       if ( to4methods[iF]._nbSplits == 0 )
2080       {
2081         switch ( iF ) {
2082         case 0:
2083           to4methods[iF]._connectivity = theHexTo4Prisms_BT;
2084           to4methods[iF]._faceBaryNode[ 0 ] = 0;
2085           to4methods[iF]._faceBaryNode[ 1 ] = 0;
2086           break;
2087         case 1:
2088           to4methods[iF]._connectivity = theHexTo4Prisms_LR;
2089           to4methods[iF]._faceBaryNode[ 2 ] = 0;
2090           to4methods[iF]._faceBaryNode[ 4 ] = 0;
2091           break;
2092         case 2:
2093           to4methods[iF]._connectivity = theHexTo4Prisms_FB;
2094           to4methods[iF]._faceBaryNode[ 3 ] = 0;
2095           to4methods[iF]._faceBaryNode[ 5 ] = 0;
2096           break;
2097         default: return to4methods[3];
2098         }
2099         to4methods[iF]._nbSplits  = 4;
2100         to4methods[iF]._nbCorners = 6;
2101       }
2102       return to4methods[iF];
2103     }
2104     // else if ( methodFlags == HEXA_TO_2_PRISMS )
2105
2106     TSplitMethod method;
2107
2108     const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2109
2110     const int nbVariants = 2, nbSplits = 2;
2111     const int** connVariants = 0;
2112     switch ( iF ) {
2113     case 0: connVariants = theHexTo2Prisms_BT; break;
2114     case 1: connVariants = theHexTo2Prisms_LR; break;
2115     case 2: connVariants = theHexTo2Prisms_FB; break;
2116     default: return method;
2117     }
2118
2119     // look for prisms adjacent via facetToSplit and an opposite one
2120     for ( int is2nd = 0; is2nd < 2; ++is2nd )
2121     {
2122       int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2123       int nbNodes = vol.NbFaceNodes( iFacet ) / iQ;
2124       if ( nbNodes != 4 ) return method;
2125
2126       const int* nInd = vol.GetFaceNodesIndices( iFacet );
2127       TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2128       TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2129       TTriangleFacet* t;
2130       if      ( t012.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2131         t = &t012;
2132       else if ( t123.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2133         t = &t123;
2134       else
2135         continue;
2136
2137       // there are adjacent prism
2138       for ( int variant = 0; variant < nbVariants; ++variant )
2139       {
2140         // check method compliance with adjacent prisms,
2141         // the found prism facets must be among facets of prisms described by current method
2142         method._nbSplits     = nbSplits;
2143         method._nbCorners    = 6;
2144         method._connectivity = connVariants[ variant ];
2145         if ( method.hasFacet( *t ))
2146           return method;
2147       }
2148     }
2149
2150     // No adjacent prisms. Select a variant with a best aspect ratio.
2151
2152     double badness[2] = { 0., 0. };
2153     static SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
2154     const SMDS_MeshNode** nodes = vol.GetNodes();
2155     for ( int variant = 0; variant < nbVariants; ++variant )
2156       for ( int is2nd = 0; is2nd < 2; ++is2nd )
2157       {
2158         int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2159         const int*             nInd = vol.GetFaceNodesIndices( iFacet );
2160
2161         method._connectivity = connVariants[ variant ];
2162         TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2163         TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2164         TTriangleFacet* t = ( method.hasFacet( t012 )) ? & t012 : & t123;
2165
2166         SMDS_FaceOfNodes tria ( nodes[ t->_n1 ],
2167                                 nodes[ t->_n2 ],
2168                                 nodes[ t->_n3 ] );
2169         badness[ variant ] += getBadRate( &tria, aspectRatio );
2170       }
2171     const int iBetter = ( badness[1] < badness[0] && badness[0]-badness[1] > 0.1 * badness[0] );
2172
2173     method._nbSplits     = nbSplits;
2174     method._nbCorners    = 6;
2175     method._connectivity = connVariants[ iBetter ];
2176
2177     return method;
2178   }
2179
2180   //================================================================================
2181   /*!
2182    * \brief Check if there is a tetraherdon adjacent to the given element via this facet
2183    */
2184   //================================================================================
2185
2186   bool TTriangleFacet::hasAdjacentVol( const SMDS_MeshElement*    elem,
2187                                        const SMDSAbs_GeometryType geom ) const
2188   {
2189     // find the tetrahedron including the three nodes of facet
2190     const SMDS_MeshNode* n1 = elem->GetNode(_n1);
2191     const SMDS_MeshNode* n2 = elem->GetNode(_n2);
2192     const SMDS_MeshNode* n3 = elem->GetNode(_n3);
2193     SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
2194     while ( volIt1->more() )
2195     {
2196       const SMDS_MeshElement* v = volIt1->next();
2197       if ( v->GetGeomType() != geom )
2198         continue;
2199       const int lastCornerInd = v->NbCornerNodes() - 1;
2200       if ( v->IsQuadratic() && v->GetNodeIndex( n1 ) > lastCornerInd )
2201         continue; // medium node not allowed
2202       const int ind2 = v->GetNodeIndex( n2 );
2203       if ( ind2 < 0 || lastCornerInd < ind2 )
2204         continue;
2205       const int ind3 = v->GetNodeIndex( n3 );
2206       if ( ind3 < 0 || lastCornerInd < ind3 )
2207         continue;
2208       return true;
2209     }
2210     return false;
2211   }
2212
2213   //=======================================================================
2214   /*!
2215    * \brief A key of a face of volume
2216    */
2217   //=======================================================================
2218
2219   struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
2220   {
2221     TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
2222     {
2223       TIDSortedNodeSet sortedNodes;
2224       const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2225       int nbNodes = vol.NbFaceNodes( iF );
2226       const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
2227       for ( int i = 0; i < nbNodes; i += iQ )
2228         sortedNodes.insert( fNodes[i] );
2229       TIDSortedNodeSet::iterator n = sortedNodes.begin();
2230       first.first   = (*(n++))->GetID();
2231       first.second  = (*(n++))->GetID();
2232       second.first  = (*(n++))->GetID();
2233       second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
2234     }
2235   };
2236 } // namespace
2237
2238 //=======================================================================
2239 //function : SplitVolumes
2240 //purpose  : Split volume elements into tetrahedra or prisms.
2241 //           If facet ID < 0, element is split into tetrahedra,
2242 //           else a hexahedron is split into prisms so that the given facet is
2243 //           split into triangles
2244 //=======================================================================
2245
2246 void SMESH_MeshEditor::SplitVolumes (const TFacetOfElem & theElems,
2247                                      const int            theMethodFlags)
2248 {
2249   SMDS_VolumeTool    volTool;
2250   SMESH_MesherHelper helper( *GetMesh()), fHelper(*GetMesh());
2251   fHelper.ToFixNodeParameters( true );
2252
2253   SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
2254   SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
2255
2256   SMESH_SequenceOfElemPtr newNodes, newElems;
2257
2258   // map face of volume to it's baricenrtic node
2259   map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
2260   double bc[3];
2261   vector<const SMDS_MeshElement* > splitVols;
2262
2263   TFacetOfElem::const_iterator elem2facet = theElems.begin();
2264   for ( ; elem2facet != theElems.end(); ++elem2facet )
2265   {
2266     const SMDS_MeshElement* elem = elem2facet->first;
2267     const int       facetToSplit = elem2facet->second;
2268     if ( elem->GetType() != SMDSAbs_Volume )
2269       continue;
2270     const SMDSAbs_EntityType geomType = elem->GetEntityType();
2271     if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
2272       continue;
2273
2274     if ( !volTool.Set( elem, /*ignoreCentralNodes=*/false )) continue; // strange...
2275
2276     TSplitMethod splitMethod = ( facetToSplit < 0  ?
2277                                  getTetraSplitMethod( volTool, theMethodFlags ) :
2278                                  getPrismSplitMethod( volTool, theMethodFlags, facetToSplit ));
2279         if ( splitMethod._nbSplits < 1 ) continue;
2280
2281     // find submesh to add new tetras to
2282     if ( !subMesh || !subMesh->Contains( elem ))
2283     {
2284       int shapeID = FindShape( elem );
2285       helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
2286       subMesh = GetMeshDS()->MeshElements( shapeID );
2287     }
2288     int iQ;
2289     if ( elem->IsQuadratic() )
2290     {
2291       iQ = 2;
2292       // add quadratic links to the helper
2293       for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2294       {
2295         const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
2296         int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
2297         for ( int iN = 0; iN < nbN; iN += iQ )
2298           helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
2299       }
2300       helper.SetIsQuadratic( true );
2301     }
2302     else
2303     {
2304       iQ = 1;
2305       helper.SetIsQuadratic( false );
2306     }
2307     vector<const SMDS_MeshNode*> nodes( volTool.GetNodes(),
2308                                         volTool.GetNodes() + elem->NbNodes() );
2309     helper.SetElementsOnShape( true );
2310     if ( splitMethod._baryNode )
2311     {
2312       // make a node at barycenter
2313       volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
2314       SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
2315       nodes.push_back( gcNode );
2316       newNodes.push_back( gcNode );
2317     }
2318     if ( !splitMethod._faceBaryNode.empty() )
2319     {
2320       // make or find baricentric nodes of faces
2321       map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
2322       for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
2323       {
2324         map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
2325           volFace2BaryNode.insert
2326           ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
2327         if ( !f_n->second )
2328         {
2329           volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
2330           newNodes.push_back( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
2331         }
2332         nodes.push_back( iF_n->second = f_n->second );
2333       }
2334     }
2335
2336     // make new volumes
2337     splitVols.resize( splitMethod._nbSplits ); // splits of a volume
2338     const int* volConn = splitMethod._connectivity;
2339     if ( splitMethod._nbCorners == 4 ) // tetra
2340       for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2341         newElems.push_back( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2342                                                                nodes[ volConn[1] ],
2343                                                                nodes[ volConn[2] ],
2344                                                                nodes[ volConn[3] ]));
2345     else // prisms
2346       for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2347         newElems.push_back( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2348                                                                nodes[ volConn[1] ],
2349                                                                nodes[ volConn[2] ],
2350                                                                nodes[ volConn[3] ],
2351                                                                nodes[ volConn[4] ],
2352                                                                nodes[ volConn[5] ]));
2353
2354     ReplaceElemInGroups( elem, splitVols, GetMeshDS() );
2355
2356     // Split faces on sides of the split volume
2357
2358     const SMDS_MeshNode** volNodes = volTool.GetNodes();
2359     for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2360     {
2361       const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
2362       if ( nbNodes < 4 ) continue;
2363
2364       // find an existing face
2365       vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
2366                                            volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
2367       while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
2368                                                                        /*noMedium=*/false))
2369       {
2370         // make triangles
2371         helper.SetElementsOnShape( false );
2372         vector< const SMDS_MeshElement* > triangles;
2373
2374         // find submesh to add new triangles in
2375         if ( !fSubMesh || !fSubMesh->Contains( face ))
2376         {
2377           int shapeID = FindShape( face );
2378           fSubMesh = GetMeshDS()->MeshElements( shapeID );
2379         }
2380         map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
2381         if ( iF_n != splitMethod._faceBaryNode.end() )
2382         {
2383           const SMDS_MeshNode *baryNode = iF_n->second;
2384           for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
2385           {
2386             const SMDS_MeshNode* n1 = fNodes[iN];
2387             const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
2388             const SMDS_MeshNode *n3 = baryNode;
2389             if ( !volTool.IsFaceExternal( iF ))
2390               swap( n2, n3 );
2391             triangles.push_back( helper.AddFace( n1,n2,n3 ));
2392           }
2393           if ( fSubMesh ) // update position of the bary node on geometry
2394           {
2395             if ( subMesh )
2396               subMesh->RemoveNode( baryNode );
2397             GetMeshDS()->SetNodeOnFace( baryNode, fSubMesh->GetID() );
2398             const TopoDS_Shape& s = GetMeshDS()->IndexToShape( fSubMesh->GetID() );
2399             if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE )
2400             {
2401               fHelper.SetSubShape( s );
2402               gp_XY uv( 1e100, 1e100 );
2403               double distXYZ[4];
2404               if ( !fHelper.CheckNodeUV( TopoDS::Face( s ), baryNode,
2405                                          uv, /*tol=*/1e-7, /*force=*/true, distXYZ ) &&
2406                    uv.X() < 1e100 )
2407               {
2408                 // node is too far from the surface
2409                 GetMeshDS()->MoveNode( baryNode, distXYZ[1], distXYZ[2], distXYZ[3] );
2410                 const_cast<SMDS_MeshNode*>( baryNode )->SetPosition
2411                   ( SMDS_PositionPtr( new SMDS_FacePosition( uv.X(), uv.Y() )));
2412               }
2413             }
2414           }
2415         }
2416         else
2417         {
2418           // among possible triangles create ones described by split method
2419           const int* nInd = volTool.GetFaceNodesIndices( iF );
2420           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2421           int iCom = 0; // common node of triangle faces to split into
2422           list< TTriangleFacet > facets;
2423           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
2424           {
2425             TTriangleFacet t012( nInd[ iQ * ( iCom                )],
2426                                  nInd[ iQ * ( (iCom+1)%nbNodes )],
2427                                  nInd[ iQ * ( (iCom+2)%nbNodes )]);
2428             TTriangleFacet t023( nInd[ iQ * ( iCom                )],
2429                                  nInd[ iQ * ( (iCom+2)%nbNodes )],
2430                                  nInd[ iQ * ( (iCom+3)%nbNodes )]);
2431             if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
2432             {
2433               facets.push_back( t012 );
2434               facets.push_back( t023 );
2435               for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
2436                 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom             )],
2437                                                   nInd[ iQ * ((iLast-1)%nbNodes )],
2438                                                   nInd[ iQ * ((iLast  )%nbNodes )]));
2439               break;
2440             }
2441           }
2442           list< TTriangleFacet >::iterator facet = facets.begin();
2443           if ( facet == facets.end() )
2444             break;
2445           for ( ; facet != facets.end(); ++facet )
2446           {
2447             if ( !volTool.IsFaceExternal( iF ))
2448               swap( facet->_n2, facet->_n3 );
2449             triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
2450                                                  volNodes[ facet->_n2 ],
2451                                                  volNodes[ facet->_n3 ]));
2452           }
2453         }
2454         for ( size_t i = 0; i < triangles.size(); ++i )
2455         {
2456           if ( !triangles[ i ]) continue;
2457           if ( fSubMesh )
2458             fSubMesh->AddElement( triangles[ i ]);
2459           newElems.push_back( triangles[ i ]);
2460         }
2461         ReplaceElemInGroups( face, triangles, GetMeshDS() );
2462         GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2463
2464       } // while a face based on facet nodes exists
2465     } // loop on volume faces to split them into triangles
2466
2467     GetMeshDS()->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2468
2469     if ( geomType == SMDSEntity_TriQuad_Hexa )
2470     {
2471       // remove medium nodes that could become free
2472       for ( int i = 20; i < volTool.NbNodes(); ++i )
2473         if ( volNodes[i]->NbInverseElements() == 0 )
2474           GetMeshDS()->RemoveNode( volNodes[i] );
2475     }
2476   } // loop on volumes to split
2477
2478   myLastCreatedNodes = newNodes;
2479   myLastCreatedElems = newElems;
2480 }
2481
2482 //=======================================================================
2483 //function : GetHexaFacetsToSplit
2484 //purpose  : For hexahedra that will be split into prisms, finds facets to
2485 //           split into triangles. Only hexahedra adjacent to the one closest
2486 //           to theFacetNormal.Location() are returned.
2487 //param [in,out] theHexas - the hexahedra
2488 //param [in]     theFacetNormal - facet normal
2489 //param [out]    theFacets - the hexahedra and found facet IDs
2490 //=======================================================================
2491
2492 void SMESH_MeshEditor::GetHexaFacetsToSplit( TIDSortedElemSet& theHexas,
2493                                              const gp_Ax1&     theFacetNormal,
2494                                              TFacetOfElem &    theFacets)
2495 {
2496 #define THIS_METHOD "SMESH_MeshEditor::GetHexaFacetsToSplit(): "
2497
2498   // Find a hexa closest to the location of theFacetNormal
2499
2500   const SMDS_MeshElement* startHex;
2501   {
2502     // get SMDS_ElemIteratorPtr on theHexas
2503     typedef const SMDS_MeshElement*                                      TValue;
2504     typedef TIDSortedElemSet::iterator                                   TSetIterator;
2505     typedef SMDS::SimpleAccessor<TValue,TSetIterator>                    TAccesor;
2506     typedef SMDS_MeshElement::GeomFilter                                 TFilter;
2507     typedef SMDS_SetIterator < TValue, TSetIterator, TAccesor, TFilter > TElemSetIter;
2508     SMDS_ElemIteratorPtr elemIt = SMDS_ElemIteratorPtr
2509       ( new TElemSetIter( theHexas.begin(),
2510                           theHexas.end(),
2511                           SMDS_MeshElement::GeomFilter( SMDSGeom_HEXA )));
2512
2513     SMESH_ElementSearcher* searcher =
2514       SMESH_MeshAlgos::GetElementSearcher( *myMesh->GetMeshDS(), elemIt );
2515
2516     startHex = searcher->FindClosestTo( theFacetNormal.Location(), SMDSAbs_Volume );
2517
2518     delete searcher;
2519
2520     if ( !startHex )
2521       throw SALOME_Exception( THIS_METHOD "startHex not found");
2522   }
2523
2524   // Select a facet of startHex by theFacetNormal
2525
2526   SMDS_VolumeTool vTool( startHex );
2527   double norm[3], dot, maxDot = 0;
2528   int facetID = -1;
2529   for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2530     if ( vTool.GetFaceNormal( iF, norm[0], norm[1], norm[2] ))
2531     {
2532       dot = Abs( theFacetNormal.Direction().Dot( gp_Dir( norm[0], norm[1], norm[2] )));
2533       if ( dot > maxDot )
2534       {
2535         facetID = iF;
2536         maxDot = dot;
2537       }
2538     }
2539   if ( facetID < 0 )
2540     throw SALOME_Exception( THIS_METHOD "facet of startHex not found");
2541
2542   // Fill theFacets starting from facetID of startHex
2543
2544   // facets used for searching of volumes adjacent to already treated ones
2545   typedef pair< TFacetOfElem::iterator, int > TElemFacets;
2546   typedef map< TVolumeFaceKey, TElemFacets  > TFacetMap;
2547   TFacetMap facetsToCheck;
2548
2549   set<const SMDS_MeshNode*> facetNodes;
2550   const SMDS_MeshElement*   curHex;
2551
2552   const bool allHex = ((int) theHexas.size() == myMesh->NbHexas() );
2553
2554   while ( startHex )
2555   {
2556     // move in two directions from startHex via facetID
2557     for ( int is2nd = 0; is2nd < 2; ++is2nd )
2558     {
2559       curHex       = startHex;
2560       int curFacet = facetID;
2561       if ( is2nd ) // do not treat startHex twice
2562       {
2563         vTool.Set( curHex );
2564         if ( vTool.IsFreeFace( curFacet, &curHex ))
2565         {
2566           curHex = 0;
2567         }
2568         else
2569         {
2570           vTool.GetFaceNodes( curFacet, facetNodes );
2571           vTool.Set( curHex );
2572           curFacet = vTool.GetFaceIndex( facetNodes );
2573         }
2574       }
2575       while ( curHex )
2576       {
2577         // store a facet to split
2578         if ( curHex->GetGeomType() != SMDSGeom_HEXA )
2579         {
2580           theFacets.insert( make_pair( curHex, -1 ));
2581           break;
2582         }
2583         if ( !allHex && !theHexas.count( curHex ))
2584           break;
2585
2586         pair< TFacetOfElem::iterator, bool > facetIt2isNew =
2587           theFacets.insert( make_pair( curHex, curFacet ));
2588         if ( !facetIt2isNew.second )
2589           break;
2590
2591         // remember not-to-split facets in facetsToCheck
2592         int oppFacet = vTool.GetOppFaceIndexOfHex( curFacet );
2593         for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2594         {
2595           if ( iF == curFacet && iF == oppFacet )
2596             continue;
2597           TVolumeFaceKey facetKey ( vTool, iF );
2598           TElemFacets    elemFacet( facetIt2isNew.first, iF );
2599           pair< TFacetMap::iterator, bool > it2isnew =
2600             facetsToCheck.insert( make_pair( facetKey, elemFacet ));
2601           if ( !it2isnew.second )
2602             facetsToCheck.erase( it2isnew.first ); // adjacent hex already checked
2603         }
2604         // pass to a volume adjacent via oppFacet
2605         if ( vTool.IsFreeFace( oppFacet, &curHex ))
2606         {
2607           curHex = 0;
2608         }
2609         else
2610         {
2611           // get a new curFacet
2612           vTool.GetFaceNodes( oppFacet, facetNodes );
2613           vTool.Set( curHex );
2614           curFacet = vTool.GetFaceIndex( facetNodes, /*hint=*/curFacet );
2615         }
2616       }
2617     } // move in two directions from startHex via facetID
2618
2619     // Find a new startHex by facetsToCheck
2620
2621     startHex = 0;
2622     facetID  = -1;
2623     TFacetMap::iterator fIt = facetsToCheck.begin();
2624     while ( !startHex && fIt != facetsToCheck.end() )
2625     {
2626       const TElemFacets&  elemFacets = fIt->second;
2627       const SMDS_MeshElement*    hex = elemFacets.first->first;
2628       int                 splitFacet = elemFacets.first->second;
2629       int               lateralFacet = elemFacets.second;
2630       facetsToCheck.erase( fIt );
2631       fIt = facetsToCheck.begin();
2632
2633       vTool.Set( hex );
2634       if ( vTool.IsFreeFace( lateralFacet, &curHex ) || 
2635            curHex->GetGeomType() != SMDSGeom_HEXA )
2636         continue;
2637       if ( !allHex && !theHexas.count( curHex ))
2638         continue;
2639
2640       startHex = curHex;
2641
2642       // find a facet of startHex to split
2643
2644       set<const SMDS_MeshNode*> lateralNodes;
2645       vTool.GetFaceNodes( lateralFacet, lateralNodes );
2646       vTool.GetFaceNodes( splitFacet,   facetNodes );
2647       int oppLateralFacet = vTool.GetOppFaceIndexOfHex( lateralFacet );
2648       vTool.Set( startHex );
2649       lateralFacet = vTool.GetFaceIndex( lateralNodes, oppLateralFacet );
2650
2651       // look for a facet of startHex having common nodes with facetNodes
2652       // but not lateralFacet
2653       for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2654       {
2655         if ( iF == lateralFacet )
2656           continue;
2657         int nbCommonNodes = 0;
2658         const SMDS_MeshNode** nn = vTool.GetFaceNodes( iF );
2659         for ( int iN = 0, nbN = vTool.NbFaceNodes( iF ); iN < nbN; ++iN )
2660           nbCommonNodes += facetNodes.count( nn[ iN ]);
2661
2662         if ( nbCommonNodes >= 2 )
2663         {
2664           facetID = iF;
2665           break;
2666         }
2667       }
2668       if ( facetID < 0 )
2669         throw SALOME_Exception( THIS_METHOD "facet of a new startHex not found");
2670     }
2671   } //   while ( startHex )
2672
2673   return;
2674 }
2675
2676 namespace
2677 {
2678   //================================================================================
2679   /*!
2680    * \brief Selects nodes of several elements according to a given interlace
2681    *  \param [in] srcNodes - nodes to select from
2682    *  \param [out] tgtNodesVec - array of nodes of several elements to fill in
2683    *  \param [in] interlace - indices of nodes for all elements
2684    *  \param [in] nbElems - nb of elements
2685    *  \param [in] nbNodes - nb of nodes in each element
2686    *  \param [in] mesh - the mesh
2687    *  \param [out] elemQueue - a list to push elements found by the selected nodes
2688    *  \param [in] type - type of elements to look for
2689    */
2690   //================================================================================
2691
2692   void selectNodes( const vector< const SMDS_MeshNode* >& srcNodes,
2693                     vector< const SMDS_MeshNode* >*       tgtNodesVec,
2694                     const int*                            interlace,
2695                     const int                             nbElems,
2696                     const int                             nbNodes,
2697                     SMESHDS_Mesh*                         mesh = 0,
2698                     list< const SMDS_MeshElement* >*      elemQueue=0,
2699                     SMDSAbs_ElementType                   type=SMDSAbs_All)
2700   {
2701     for ( int iE = 0; iE < nbElems; ++iE )
2702     {
2703       vector< const SMDS_MeshNode* >& elemNodes = tgtNodesVec[iE];
2704       const int*                         select = & interlace[iE*nbNodes];
2705       elemNodes.resize( nbNodes );
2706       for ( int iN = 0; iN < nbNodes; ++iN )
2707         elemNodes[iN] = srcNodes[ select[ iN ]];
2708     }
2709     const SMDS_MeshElement* e;
2710     if ( elemQueue )
2711       for ( int iE = 0; iE < nbElems; ++iE )
2712         if (( e = mesh->FindElement( tgtNodesVec[iE], type, /*noMedium=*/false)))
2713           elemQueue->push_back( e );
2714   }
2715 }
2716
2717 //=======================================================================
2718 /*
2719  * Split bi-quadratic elements into linear ones without creation of additional nodes
2720  *   - bi-quadratic triangle will be split into 3 linear quadrangles;
2721  *   - bi-quadratic quadrangle will be split into 4 linear quadrangles;
2722  *   - tri-quadratic hexahedron will be split into 8 linear hexahedra;
2723  *   Quadratic elements of lower dimension  adjacent to the split bi-quadratic element
2724  *   will be split in order to keep the mesh conformal.
2725  *  \param elems - elements to split
2726  */
2727 //=======================================================================
2728
2729 void SMESH_MeshEditor::SplitBiQuadraticIntoLinear(TIDSortedElemSet& theElems)
2730 {
2731   vector< const SMDS_MeshNode* > elemNodes(27), subNodes[12], splitNodes[8];
2732   vector<const SMDS_MeshElement* > splitElems;
2733   list< const SMDS_MeshElement* > elemQueue;
2734   list< const SMDS_MeshElement* >::iterator elemIt;
2735
2736   SMESHDS_Mesh * mesh = GetMeshDS();
2737   ElemFeatures *elemType, hexaType(SMDSAbs_Volume), quadType(SMDSAbs_Face), segType(SMDSAbs_Edge);
2738   int nbElems, nbNodes;
2739
2740   TIDSortedElemSet::iterator elemSetIt = theElems.begin();
2741   for ( ; elemSetIt != theElems.end(); ++elemSetIt )
2742   {
2743     elemQueue.clear();
2744     elemQueue.push_back( *elemSetIt );
2745     for ( elemIt = elemQueue.begin(); elemIt != elemQueue.end(); ++elemIt )
2746     {
2747       const SMDS_MeshElement* elem = *elemIt;
2748       switch( elem->GetEntityType() )
2749       {
2750       case SMDSEntity_TriQuad_Hexa: // HEX27
2751       {
2752         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2753         nbElems  = nbNodes = 8;
2754         elemType = & hexaType;
2755
2756         // get nodes for new elements
2757         static int vInd[8][8] = {{ 0,8,20,11,   16,21,26,24 },
2758                                  { 1,9,20,8,    17,22,26,21 },
2759                                  { 2,10,20,9,   18,23,26,22 },
2760                                  { 3,11,20,10,  19,24,26,23 },
2761                                  { 16,21,26,24, 4,12,25,15  },
2762                                  { 17,22,26,21, 5,13,25,12  },
2763                                  { 18,23,26,22, 6,14,25,13  },
2764                                  { 19,24,26,23, 7,15,25,14  }};
2765         selectNodes( elemNodes, & splitNodes[0], &vInd[0][0], nbElems, nbNodes );
2766
2767         // add boundary faces to elemQueue
2768         static int fInd[6][9] = {{ 0,1,2,3, 8,9,10,11,   20 },
2769                                  { 4,5,6,7, 12,13,14,15, 25 },
2770                                  { 0,1,5,4, 8,17,12,16,  21 },
2771                                  { 1,2,6,5, 9,18,13,17,  22 },
2772                                  { 2,3,7,6, 10,19,14,18, 23 },
2773                                  { 3,0,4,7, 11,16,15,19, 24 }};
2774         selectNodes( elemNodes, & subNodes[0], &fInd[0][0], 6,9, mesh, &elemQueue, SMDSAbs_Face );
2775
2776         // add boundary segments to elemQueue
2777         static int eInd[12][3] = {{ 0,1,8 }, { 1,2,9 }, { 2,3,10 }, { 3,0,11 },
2778                                   { 4,5,12}, { 5,6,13}, { 6,7,14 }, { 7,4,15 },
2779                                   { 0,4,16}, { 1,5,17}, { 2,6,18 }, { 3,7,19 }};
2780         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 12,3, mesh, &elemQueue, SMDSAbs_Edge );
2781         break;
2782       }
2783       case SMDSEntity_BiQuad_Triangle: // TRIA7
2784       {
2785         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2786         nbElems = 3;
2787         nbNodes = 4;
2788         elemType = & quadType;
2789
2790         // get nodes for new elements
2791         static int fInd[3][4] = {{ 0,3,6,5 }, { 1,4,6,3 }, { 2,5,6,4 }};
2792         selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2793
2794         // add boundary segments to elemQueue
2795         static int eInd[3][3] = {{ 0,1,3 }, { 1,2,4 }, { 2,0,5 }};
2796         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 3,3, mesh, &elemQueue, SMDSAbs_Edge );
2797         break;
2798       }
2799       case SMDSEntity_BiQuad_Quadrangle: // QUAD9
2800       {
2801         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2802         nbElems = 4;
2803         nbNodes = 4;
2804         elemType = & quadType;
2805
2806         // get nodes for new elements
2807         static int fInd[4][4] = {{ 0,4,8,7 }, { 1,5,8,4 }, { 2,6,8,5 }, { 3,7,8,6 }};
2808         selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2809
2810         // add boundary segments to elemQueue
2811         static int eInd[4][3] = {{ 0,1,4 }, { 1,2,5 }, { 2,3,6 }, { 3,0,7 }};
2812         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 4,3, mesh, &elemQueue, SMDSAbs_Edge );
2813         break;
2814       }
2815       case SMDSEntity_Quad_Edge:
2816       {
2817         if ( elemIt == elemQueue.begin() )
2818           continue; // an elem is in theElems
2819         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2820         nbElems = 2;
2821         nbNodes = 2;
2822         elemType = & segType;
2823
2824         // get nodes for new elements
2825         static int eInd[2][2] = {{ 0,2 }, { 2,1 }};
2826         selectNodes( elemNodes, & splitNodes[0], &eInd[0][0], nbElems, nbNodes );
2827         break;
2828       }
2829       default: continue;
2830       } // switch( elem->GetEntityType() )
2831
2832       // Create new elements
2833
2834       SMESHDS_SubMesh* subMesh = mesh->MeshElements( elem->getshapeId() );
2835
2836       splitElems.clear();
2837
2838       //elemType->SetID( elem->GetID() ); // create an elem with the same ID as a removed one
2839       mesh->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2840       //splitElems.push_back( AddElement( splitNodes[ 0 ], *elemType ));
2841       //elemType->SetID( -1 );
2842
2843       for ( int iE = 0; iE < nbElems; ++iE )
2844         splitElems.push_back( AddElement( splitNodes[ iE ], *elemType ));
2845
2846
2847       ReplaceElemInGroups( elem, splitElems, mesh );
2848
2849       if ( subMesh )
2850         for ( size_t i = 0; i < splitElems.size(); ++i )
2851           subMesh->AddElement( splitElems[i] );
2852     }
2853   }
2854 }
2855
2856 //=======================================================================
2857 //function : AddToSameGroups
2858 //purpose  : add elemToAdd to the groups the elemInGroups belongs to
2859 //=======================================================================
2860
2861 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
2862                                         const SMDS_MeshElement* elemInGroups,
2863                                         SMESHDS_Mesh *          aMesh)
2864 {
2865   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2866   if (!groups.empty()) {
2867     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2868     for ( ; grIt != groups.end(); grIt++ ) {
2869       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2870       if ( group && group->Contains( elemInGroups ))
2871         group->SMDSGroup().Add( elemToAdd );
2872     }
2873   }
2874 }
2875
2876
2877 //=======================================================================
2878 //function : RemoveElemFromGroups
2879 //purpose  : Remove removeelem to the groups the elemInGroups belongs to
2880 //=======================================================================
2881 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2882                                              SMESHDS_Mesh *          aMesh)
2883 {
2884   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2885   if (!groups.empty())
2886   {
2887     set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2888     for (; GrIt != groups.end(); GrIt++)
2889     {
2890       SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2891       if (!grp || grp->IsEmpty()) continue;
2892       grp->SMDSGroup().Remove(removeelem);
2893     }
2894   }
2895 }
2896
2897 //================================================================================
2898 /*!
2899  * \brief Replace elemToRm by elemToAdd in the all groups
2900  */
2901 //================================================================================
2902
2903 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2904                                             const SMDS_MeshElement* elemToAdd,
2905                                             SMESHDS_Mesh *          aMesh)
2906 {
2907   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2908   if (!groups.empty()) {
2909     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2910     for ( ; grIt != groups.end(); grIt++ ) {
2911       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2912       if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2913         group->SMDSGroup().Add( elemToAdd );
2914     }
2915   }
2916 }
2917
2918 //================================================================================
2919 /*!
2920  * \brief Replace elemToRm by elemToAdd in the all groups
2921  */
2922 //================================================================================
2923
2924 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement*                elemToRm,
2925                                             const vector<const SMDS_MeshElement*>& elemToAdd,
2926                                             SMESHDS_Mesh *                         aMesh)
2927 {
2928   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2929   if (!groups.empty())
2930   {
2931     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2932     for ( ; grIt != groups.end(); grIt++ ) {
2933       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2934       if ( group && group->SMDSGroup().Remove( elemToRm ) )
2935         for ( size_t i = 0; i < elemToAdd.size(); ++i )
2936           group->SMDSGroup().Add( elemToAdd[ i ] );
2937     }
2938   }
2939 }
2940
2941 //=======================================================================
2942 //function : QuadToTri
2943 //purpose  : Cut quadrangles into triangles.
2944 //           theCrit is used to select a diagonal to cut
2945 //=======================================================================
2946
2947 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2948                                   const bool         the13Diag)
2949 {
2950   ClearLastCreated();
2951   myLastCreatedElems.reserve( theElems.size() * 2 );
2952
2953   SMESHDS_Mesh *       aMesh = GetMeshDS();
2954   Handle(Geom_Surface) surface;
2955   SMESH_MesherHelper   helper( *GetMesh() );
2956
2957   TIDSortedElemSet::iterator itElem;
2958   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
2959   {
2960     const SMDS_MeshElement* elem = *itElem;
2961     if ( !elem || elem->GetGeomType() != SMDSGeom_QUADRANGLE )
2962       continue;
2963
2964     if ( elem->NbNodes() == 4 ) {
2965       // retrieve element nodes
2966       const SMDS_MeshNode* aNodes [4];
2967       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2968       int i = 0;
2969       while ( itN->more() )
2970         aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2971
2972       int aShapeId = FindShape( elem );
2973       const SMDS_MeshElement* newElem1 = 0;
2974       const SMDS_MeshElement* newElem2 = 0;
2975       if ( the13Diag ) {
2976         newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2977         newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2978       }
2979       else {
2980         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2981         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2982       }
2983       myLastCreatedElems.push_back(newElem1);
2984       myLastCreatedElems.push_back(newElem2);
2985       // put a new triangle on the same shape and add to the same groups
2986       if ( aShapeId )
2987       {
2988         aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2989         aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2990       }
2991       AddToSameGroups( newElem1, elem, aMesh );
2992       AddToSameGroups( newElem2, elem, aMesh );
2993       aMesh->RemoveElement( elem );
2994     }
2995
2996     // Quadratic quadrangle
2997
2998     else if ( elem->NbNodes() >= 8 )
2999     {
3000       // get surface elem is on
3001       int aShapeId = FindShape( elem );
3002       if ( aShapeId != helper.GetSubShapeID() ) {
3003         surface.Nullify();
3004         TopoDS_Shape shape;
3005         if ( aShapeId > 0 )
3006           shape = aMesh->IndexToShape( aShapeId );
3007         if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
3008           TopoDS_Face face = TopoDS::Face( shape );
3009           surface = BRep_Tool::Surface( face );
3010           if ( !surface.IsNull() )
3011             helper.SetSubShape( shape );
3012         }
3013       }
3014
3015       const SMDS_MeshNode* aNodes [9]; aNodes[8] = 0;
3016       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3017       for ( int i = 0; itN->more(); ++i )
3018         aNodes[ i ] = static_cast<const SMDS_MeshNode*>( itN->next() );
3019
3020       const SMDS_MeshNode* centrNode = aNodes[8];
3021       if ( centrNode == 0 )
3022       {
3023         centrNode = helper.GetCentralNode( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3024                                            aNodes[4], aNodes[5], aNodes[6], aNodes[7],
3025                                            surface.IsNull() );
3026         myLastCreatedNodes.push_back(centrNode);
3027       }
3028
3029       // create a new element
3030       const SMDS_MeshElement* newElem1 = 0;
3031       const SMDS_MeshElement* newElem2 = 0;
3032       if ( the13Diag ) {
3033         newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
3034                                   aNodes[6], aNodes[7], centrNode );
3035         newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
3036                                   centrNode, aNodes[4], aNodes[5] );
3037       }
3038       else {
3039         newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
3040                                   aNodes[7], aNodes[4], centrNode );
3041         newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
3042                                   centrNode, aNodes[5], aNodes[6] );
3043       }
3044       myLastCreatedElems.push_back(newElem1);
3045       myLastCreatedElems.push_back(newElem2);
3046       // put a new triangle on the same shape and add to the same groups
3047       if ( aShapeId )
3048       {
3049         aMesh->SetMeshElementOnShape( newElem1, aShapeId );
3050         aMesh->SetMeshElementOnShape( newElem2, aShapeId );
3051       }
3052       AddToSameGroups( newElem1, elem, aMesh );
3053       AddToSameGroups( newElem2, elem, aMesh );
3054       aMesh->RemoveElement( elem );
3055     }
3056   }
3057
3058   return true;
3059 }
3060
3061 //=======================================================================
3062 //function : getAngle
3063 //purpose  :
3064 //=======================================================================
3065
3066 double getAngle(const SMDS_MeshElement * tr1,
3067                 const SMDS_MeshElement * tr2,
3068                 const SMDS_MeshNode *    n1,
3069                 const SMDS_MeshNode *    n2)
3070 {
3071   double angle = 2. * M_PI; // bad angle
3072
3073   // get normals
3074   SMESH::Controls::TSequenceOfXYZ P1, P2;
3075   if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
3076        !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
3077     return angle;
3078   gp_Vec N1,N2;
3079   if(!tr1->IsQuadratic())
3080     N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
3081   else
3082     N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
3083   if ( N1.SquareMagnitude() <= gp::Resolution() )
3084     return angle;
3085   if(!tr2->IsQuadratic())
3086     N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
3087   else
3088     N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
3089   if ( N2.SquareMagnitude() <= gp::Resolution() )
3090     return angle;
3091
3092   // find the first diagonal node n1 in the triangles:
3093   // take in account a diagonal link orientation
3094   const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
3095   for ( int t = 0; t < 2; t++ ) {
3096     SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
3097     int i = 0, iDiag = -1;
3098     while ( it->more()) {
3099       const SMDS_MeshElement *n = it->next();
3100       if ( n == n1 || n == n2 ) {
3101         if ( iDiag < 0)
3102           iDiag = i;
3103         else {
3104           if ( i - iDiag == 1 )
3105             nFirst[ t ] = ( n == n1 ? n2 : n1 );
3106           else
3107             nFirst[ t ] = n;
3108           break;
3109         }
3110       }
3111       i++;
3112     }
3113   }
3114   if ( nFirst[ 0 ] == nFirst[ 1 ] )
3115     N2.Reverse();
3116
3117   angle = N1.Angle( N2 );
3118   //SCRUTE( angle );
3119   return angle;
3120 }
3121
3122 // =================================================
3123 // class generating a unique ID for a pair of nodes
3124 // and able to return nodes by that ID
3125 // =================================================
3126 class LinkID_Gen {
3127 public:
3128
3129   LinkID_Gen( const SMESHDS_Mesh* theMesh )
3130     :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
3131   {}
3132
3133   long GetLinkID (const SMDS_MeshNode * n1,
3134                   const SMDS_MeshNode * n2) const
3135   {
3136     return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
3137   }
3138
3139   bool GetNodes (const long             theLinkID,
3140                  const SMDS_MeshNode* & theNode1,
3141                  const SMDS_MeshNode* & theNode2) const
3142   {
3143     theNode1 = myMesh->FindNode( theLinkID / myMaxID );
3144     if ( !theNode1 ) return false;
3145     theNode2 = myMesh->FindNode( theLinkID % myMaxID );
3146     if ( !theNode2 ) return false;
3147     return true;
3148   }
3149
3150 private:
3151   LinkID_Gen();
3152   const SMESHDS_Mesh* myMesh;
3153   long                myMaxID;
3154 };
3155
3156
3157 //=======================================================================
3158 //function : TriToQuad
3159 //purpose  : Fuse neighbour triangles into quadrangles.
3160 //           theCrit is used to select a neighbour to fuse with.
3161 //           theMaxAngle is a max angle between element normals at which
3162 //           fusion is still performed.
3163 //=======================================================================
3164
3165 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet &                   theElems,
3166                                   SMESH::Controls::NumericalFunctorPtr theCrit,
3167                                   const double                         theMaxAngle)
3168 {
3169   ClearLastCreated();
3170   myLastCreatedElems.reserve( theElems.size() / 2 );
3171
3172   if ( !theCrit.get() )
3173     return false;
3174
3175   SMESHDS_Mesh * aMesh = GetMeshDS();
3176
3177   // Prepare data for algo: build
3178   // 1. map of elements with their linkIDs
3179   // 2. map of linkIDs with their elements
3180
3181   map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
3182   map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
3183   map< const SMDS_MeshElement*, set< SMESH_TLink > >  mapEl_setLi;
3184   map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
3185
3186   TIDSortedElemSet::iterator itElem;
3187   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
3188   {
3189     const SMDS_MeshElement* elem = *itElem;
3190     if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
3191     bool IsTria = ( elem->NbCornerNodes()==3 );
3192     if (!IsTria) continue;
3193
3194     // retrieve element nodes
3195     const SMDS_MeshNode* aNodes [4];
3196     SMDS_NodeIteratorPtr itN = elem->nodeIterator();
3197     int i = 0;
3198     while ( i < 3 )
3199       aNodes[ i++ ] = itN->next();
3200     aNodes[ 3 ] = aNodes[ 0 ];
3201
3202     // fill maps
3203     for ( i = 0; i < 3; i++ ) {
3204       SMESH_TLink link( aNodes[i], aNodes[i+1] );
3205       // check if elements sharing a link can be fused
3206       itLE = mapLi_listEl.find( link );
3207       if ( itLE != mapLi_listEl.end() ) {
3208         if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
3209           continue;
3210         const SMDS_MeshElement* elem2 = (*itLE).second.front();
3211         //if ( FindShape( elem ) != FindShape( elem2 ))
3212         //  continue; // do not fuse triangles laying on different shapes
3213         if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
3214           continue; // avoid making badly shaped quads
3215         (*itLE).second.push_back( elem );
3216       }
3217       else {
3218         mapLi_listEl[ link ].push_back( elem );
3219       }
3220       mapEl_setLi [ elem ].insert( link );
3221     }
3222   }
3223   // Clean the maps from the links shared by a sole element, ie
3224   // links to which only one element is bound in mapLi_listEl
3225
3226   for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
3227     int nbElems = (*itLE).second.size();
3228     if ( nbElems < 2  ) {
3229       const SMDS_MeshElement* elem = (*itLE).second.front();
3230       SMESH_TLink link = (*itLE).first;
3231       mapEl_setLi[ elem ].erase( link );
3232       if ( mapEl_setLi[ elem ].empty() )
3233         mapEl_setLi.erase( elem );
3234     }
3235   }
3236
3237   // Algo: fuse triangles into quadrangles
3238
3239   while ( ! mapEl_setLi.empty() ) {
3240     // Look for the start element:
3241     // the element having the least nb of shared links
3242     const SMDS_MeshElement* startElem = 0;
3243     int minNbLinks = 4;
3244     for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
3245       int nbLinks = (*itEL).second.size();
3246       if ( nbLinks < minNbLinks ) {
3247         startElem = (*itEL).first;
3248         minNbLinks = nbLinks;
3249         if ( minNbLinks == 1 )
3250           break;
3251       }
3252     }
3253
3254     // search elements to fuse starting from startElem or links of elements
3255     // fused earlyer - startLinks
3256     list< SMESH_TLink > startLinks;
3257     while ( startElem || !startLinks.empty() ) {
3258       while ( !startElem && !startLinks.empty() ) {
3259         // Get an element to start, by a link
3260         SMESH_TLink linkId = startLinks.front();
3261         startLinks.pop_front();
3262         itLE = mapLi_listEl.find( linkId );
3263         if ( itLE != mapLi_listEl.end() ) {
3264           list< const SMDS_MeshElement* > & listElem = (*itLE).second;
3265           list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
3266           for ( ; itE != listElem.end() ; itE++ )
3267             if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
3268               startElem = (*itE);
3269           mapLi_listEl.erase( itLE );
3270         }
3271       }
3272
3273       if ( startElem ) {
3274         // Get candidates to be fused
3275         const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
3276         const SMESH_TLink *link12 = 0, *link13 = 0;
3277         startElem = 0;
3278         ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
3279         set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
3280         ASSERT( !setLi.empty() );
3281         set< SMESH_TLink >::iterator itLi;
3282         for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
3283         {
3284           const SMESH_TLink & link = (*itLi);
3285           itLE = mapLi_listEl.find( link );
3286           if ( itLE == mapLi_listEl.end() )
3287             continue;
3288
3289           const SMDS_MeshElement* elem = (*itLE).second.front();
3290           if ( elem == tr1 )
3291             elem = (*itLE).second.back();
3292           mapLi_listEl.erase( itLE );
3293           if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
3294             continue;
3295           if ( tr2 ) {
3296             tr3 = elem;
3297             link13 = &link;
3298           }
3299           else {
3300             tr2 = elem;
3301             link12 = &link;
3302           }
3303
3304           // add other links of elem to list of links to re-start from
3305           set< SMESH_TLink >& links = mapEl_setLi[ elem ];
3306           set< SMESH_TLink >::iterator it;
3307           for ( it = links.begin(); it != links.end(); it++ ) {
3308             const SMESH_TLink& link2 = (*it);
3309             if ( link2 != link )
3310               startLinks.push_back( link2 );
3311           }
3312         }
3313
3314         // Get nodes of possible quadrangles
3315         const SMDS_MeshNode *n12 [4], *n13 [4];
3316         bool Ok12 = false, Ok13 = false;
3317         const SMDS_MeshNode *linkNode1, *linkNode2;
3318         if(tr2) {
3319           linkNode1 = link12->first;
3320           linkNode2 = link12->second;
3321           if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
3322             Ok12 = true;
3323         }
3324         if(tr3) {
3325           linkNode1 = link13->first;
3326           linkNode2 = link13->second;
3327           if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
3328             Ok13 = true;
3329         }
3330
3331         // Choose a pair to fuse
3332         if ( Ok12 && Ok13 ) {
3333           SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
3334           SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
3335           double aBadRate12 = getBadRate( &quad12, theCrit );
3336           double aBadRate13 = getBadRate( &quad13, theCrit );
3337           if (  aBadRate13 < aBadRate12 )
3338             Ok12 = false;
3339           else
3340             Ok13 = false;
3341         }
3342
3343         // Make quadrangles
3344         // and remove fused elems and remove links from the maps
3345         mapEl_setLi.erase( tr1 );
3346         if ( Ok12 )
3347         {
3348           mapEl_setLi.erase( tr2 );
3349           mapLi_listEl.erase( *link12 );
3350           if ( tr1->NbNodes() == 3 )
3351           {
3352             const SMDS_MeshElement* newElem = 0;
3353             newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
3354             myLastCreatedElems.push_back(newElem);
3355             AddToSameGroups( newElem, tr1, aMesh );
3356             int aShapeId = tr1->getshapeId();
3357             if ( aShapeId )
3358               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3359             aMesh->RemoveElement( tr1 );
3360             aMesh->RemoveElement( tr2 );
3361           }
3362           else {
3363             vector< const SMDS_MeshNode* > N1;
3364             vector< const SMDS_MeshNode* > N2;
3365             getNodesFromTwoTria(tr1,tr2,N1,N2);
3366             // now we receive following N1 and N2 (using numeration as in image in InverseDiag())
3367             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
3368             // i.e. first nodes from both arrays form a new diagonal
3369             const SMDS_MeshNode* aNodes[8];
3370             aNodes[0] = N1[0];
3371             aNodes[1] = N1[1];
3372             aNodes[2] = N2[0];
3373             aNodes[3] = N2[1];
3374             aNodes[4] = N1[3];
3375             aNodes[5] = N2[5];
3376             aNodes[6] = N2[3];
3377             aNodes[7] = N1[5];
3378             const SMDS_MeshElement* newElem = 0;
3379             if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3380               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3381                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3382             else
3383               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3384                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3385             myLastCreatedElems.push_back(newElem);
3386             AddToSameGroups( newElem, tr1, aMesh );
3387             int aShapeId = tr1->getshapeId();
3388             if ( aShapeId )
3389               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3390             aMesh->RemoveElement( tr1 );
3391             aMesh->RemoveElement( tr2 );
3392             // remove middle node (9)
3393             if ( N1[4]->NbInverseElements() == 0 )
3394               aMesh->RemoveNode( N1[4] );
3395             if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3396               aMesh->RemoveNode( N1[6] );
3397             if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3398               aMesh->RemoveNode( N2[6] );
3399           }
3400         }
3401         else if ( Ok13 )
3402         {
3403           mapEl_setLi.erase( tr3 );
3404           mapLi_listEl.erase( *link13 );
3405           if ( tr1->NbNodes() == 3 ) {
3406             const SMDS_MeshElement* newElem = 0;
3407             newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
3408             myLastCreatedElems.push_back(newElem);
3409             AddToSameGroups( newElem, tr1, aMesh );
3410             int aShapeId = tr1->getshapeId();
3411             if ( aShapeId )
3412               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3413             aMesh->RemoveElement( tr1 );
3414             aMesh->RemoveElement( tr3 );
3415           }
3416           else {
3417             vector< const SMDS_MeshNode* > N1;
3418             vector< const SMDS_MeshNode* > N2;
3419             getNodesFromTwoTria(tr1,tr3,N1,N2);
3420             // now we receive following N1 and N2 (using numeration as above image)
3421             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
3422             // i.e. first nodes from both arrays form a new diagonal
3423             const SMDS_MeshNode* aNodes[8];
3424             aNodes[0] = N1[0];
3425             aNodes[1] = N1[1];
3426             aNodes[2] = N2[0];
3427             aNodes[3] = N2[1];
3428             aNodes[4] = N1[3];
3429             aNodes[5] = N2[5];
3430             aNodes[6] = N2[3];
3431             aNodes[7] = N1[5];
3432             const SMDS_MeshElement* newElem = 0;
3433             if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3434               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3435                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3436             else
3437               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3438                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3439             myLastCreatedElems.push_back(newElem);
3440             AddToSameGroups( newElem, tr1, aMesh );
3441             int aShapeId = tr1->getshapeId();
3442             if ( aShapeId )
3443               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3444             aMesh->RemoveElement( tr1 );
3445             aMesh->RemoveElement( tr3 );
3446             // remove middle node (9)
3447             if ( N1[4]->NbInverseElements() == 0 )
3448               aMesh->RemoveNode( N1[4] );
3449             if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3450               aMesh->RemoveNode( N1[6] );
3451             if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3452               aMesh->RemoveNode( N2[6] );
3453           }
3454         }
3455
3456         // Next element to fuse: the rejected one
3457         if ( tr3 )
3458           startElem = Ok12 ? tr3 : tr2;
3459
3460       } // if ( startElem )
3461     } // while ( startElem || !startLinks.empty() )
3462   } // while ( ! mapEl_setLi.empty() )
3463
3464   return true;
3465 }
3466
3467 //================================================================================
3468 /*!
3469  * \brief Return nodes linked to the given one
3470  * \param theNode - the node
3471  * \param linkedNodes - the found nodes
3472  * \param type - the type of elements to check
3473  *
3474  * Medium nodes are ignored
3475  */
3476 //================================================================================
3477
3478 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
3479                                        TIDSortedElemSet &   linkedNodes,
3480                                        SMDSAbs_ElementType  type )
3481 {
3482   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
3483   while ( elemIt->more() )
3484   {
3485     const SMDS_MeshElement* elem = elemIt->next();
3486     if(elem->GetType() == SMDSAbs_0DElement)
3487       continue;
3488
3489     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
3490     if ( elem->GetType() == SMDSAbs_Volume )
3491     {
3492       SMDS_VolumeTool vol( elem );
3493       while ( nodeIt->more() ) {
3494         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3495         if ( theNode != n && vol.IsLinked( theNode, n ))
3496           linkedNodes.insert( n );
3497       }
3498     }
3499     else
3500     {
3501       for ( int i = 0; nodeIt->more(); ++i ) {
3502         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3503         if ( n == theNode ) {
3504           int iBefore = i - 1;
3505           int iAfter  = i + 1;
3506           if ( elem->IsQuadratic() ) {
3507             int nb = elem->NbNodes() / 2;
3508             iAfter  = SMESH_MesherHelper::WrapIndex( iAfter, nb );
3509             iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
3510           }
3511           linkedNodes.insert( elem->GetNodeWrap( iAfter ));
3512           linkedNodes.insert( elem->GetNodeWrap( iBefore ));
3513         }
3514       }
3515     }
3516   }
3517 }
3518
3519 //=======================================================================
3520 //function : laplacianSmooth
3521 //purpose  : pulls theNode toward the center of surrounding nodes directly
3522 //           connected to that node along an element edge
3523 //=======================================================================
3524
3525 void laplacianSmooth(const SMDS_MeshNode*                 theNode,
3526                      const Handle(Geom_Surface)&          theSurface,
3527                      map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3528 {
3529   // find surrounding nodes
3530
3531   TIDSortedElemSet nodeSet;
3532   SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
3533
3534   // compute new coodrs
3535
3536   double coord[] = { 0., 0., 0. };
3537   TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3538   for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
3539     const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3540     if ( theSurface.IsNull() ) { // smooth in 3D
3541       coord[0] += node->X();
3542       coord[1] += node->Y();
3543       coord[2] += node->Z();
3544     }
3545     else { // smooth in 2D
3546       ASSERT( theUVMap.find( node ) != theUVMap.end() );
3547       gp_XY* uv = theUVMap[ node ];
3548       coord[0] += uv->X();
3549       coord[1] += uv->Y();
3550     }
3551   }
3552   int nbNodes = nodeSet.size();
3553   if ( !nbNodes )
3554     return;
3555   coord[0] /= nbNodes;
3556   coord[1] /= nbNodes;
3557
3558   if ( !theSurface.IsNull() ) {
3559     ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3560     theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3561     gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3562     coord[0] = p3d.X();
3563     coord[1] = p3d.Y();
3564     coord[2] = p3d.Z();
3565   }
3566   else
3567     coord[2] /= nbNodes;
3568
3569   // move node
3570
3571   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3572 }
3573
3574 //=======================================================================
3575 //function : centroidalSmooth
3576 //purpose  : pulls theNode toward the element-area-weighted centroid of the
3577 //           surrounding elements
3578 //=======================================================================
3579
3580 void centroidalSmooth(const SMDS_MeshNode*                 theNode,
3581                       const Handle(Geom_Surface)&          theSurface,
3582                       map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3583 {
3584   gp_XYZ aNewXYZ(0.,0.,0.);
3585   SMESH::Controls::Area anAreaFunc;
3586   double totalArea = 0.;
3587   int nbElems = 0;
3588
3589   // compute new XYZ
3590
3591   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3592   while ( elemIt->more() )
3593   {
3594     const SMDS_MeshElement* elem = elemIt->next();
3595     nbElems++;
3596
3597     gp_XYZ elemCenter(0.,0.,0.);
3598     SMESH::Controls::TSequenceOfXYZ aNodePoints;
3599     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3600     int nn = elem->NbNodes();
3601     if(elem->IsQuadratic()) nn = nn/2;
3602     int i=0;
3603     //while ( itN->more() ) {
3604     while ( i<nn ) {
3605       const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3606       i++;
3607       gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3608       aNodePoints.push_back( aP );
3609       if ( !theSurface.IsNull() ) { // smooth in 2D
3610         ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3611         gp_XY* uv = theUVMap[ aNode ];
3612         aP.SetCoord( uv->X(), uv->Y(), 0. );
3613       }
3614       elemCenter += aP;
3615     }
3616     double elemArea = anAreaFunc.GetValue( aNodePoints );
3617     totalArea += elemArea;
3618     elemCenter /= nn;
3619     aNewXYZ += elemCenter * elemArea;
3620   }
3621   aNewXYZ /= totalArea;
3622   if ( !theSurface.IsNull() ) {
3623     theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3624     aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3625   }
3626
3627   // move node
3628
3629   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3630 }
3631
3632 //=======================================================================
3633 //function : getClosestUV
3634 //purpose  : return UV of closest projection
3635 //=======================================================================
3636
3637 static bool getClosestUV (Extrema_GenExtPS& projector,
3638                           const gp_Pnt&     point,
3639                           gp_XY &           result)
3640 {
3641   projector.Perform( point );
3642   if ( projector.IsDone() ) {
3643     double u = 0, v = 0, minVal = DBL_MAX;
3644     for ( int i = projector.NbExt(); i > 0; i-- )
3645       if ( projector.SquareDistance( i ) < minVal ) {
3646         minVal = projector.SquareDistance( i );
3647         projector.Point( i ).Parameter( u, v );
3648       }
3649     result.SetCoord( u, v );
3650     return true;
3651   }
3652   return false;
3653 }
3654
3655 //=======================================================================
3656 //function : Smooth
3657 //purpose  : Smooth theElements during theNbIterations or until a worst
3658 //           element has aspect ratio <= theTgtAspectRatio.
3659 //           Aspect Ratio varies in range [1.0, inf].
3660 //           If theElements is empty, the whole mesh is smoothed.
3661 //           theFixedNodes contains additionally fixed nodes. Nodes built
3662 //           on edges and boundary nodes are always fixed.
3663 //=======================================================================
3664
3665 void SMESH_MeshEditor::Smooth (TIDSortedElemSet &          theElems,
3666                                set<const SMDS_MeshNode*> & theFixedNodes,
3667                                const SmoothMethod          theSmoothMethod,
3668                                const int                   theNbIterations,
3669                                double                      theTgtAspectRatio,
3670                                const bool                  the2D)
3671 {
3672   ClearLastCreated();
3673
3674   if ( theTgtAspectRatio < 1.0 )
3675     theTgtAspectRatio = 1.0;
3676
3677   const double disttol = 1.e-16;
3678
3679   SMESH::Controls::AspectRatio aQualityFunc;
3680
3681   SMESHDS_Mesh* aMesh = GetMeshDS();
3682
3683   if ( theElems.empty() ) {
3684     // add all faces to theElems
3685     SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3686     while ( fIt->more() ) {
3687       const SMDS_MeshElement* face = fIt->next();
3688       theElems.insert( theElems.end(), face );
3689     }
3690   }
3691   // get all face ids theElems are on
3692   set< int > faceIdSet;
3693   TIDSortedElemSet::iterator itElem;
3694   if ( the2D )
3695     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3696       int fId = FindShape( *itElem );
3697       // check that corresponding submesh exists and a shape is face
3698       if (fId &&
3699           faceIdSet.find( fId ) == faceIdSet.end() &&
3700           aMesh->MeshElements( fId )) {
3701         TopoDS_Shape F = aMesh->IndexToShape( fId );
3702         if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3703           faceIdSet.insert( fId );
3704       }
3705     }
3706   faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3707
3708   // ===============================================
3709   // smooth elements on each TopoDS_Face separately
3710   // ===============================================
3711
3712   SMESH_MesherHelper helper( *GetMesh() );
3713
3714   set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treat 0 fId at the end
3715   for ( ; fId != faceIdSet.rend(); ++fId )
3716   {
3717     // get face surface and submesh
3718     Handle(Geom_Surface) surface;
3719     SMESHDS_SubMesh* faceSubMesh = 0;
3720     TopoDS_Face face;
3721     double fToler2 = 0;
3722     double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3723     bool isUPeriodic = false, isVPeriodic = false;
3724     if ( *fId )
3725     {
3726       face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3727       surface = BRep_Tool::Surface( face );
3728       faceSubMesh = aMesh->MeshElements( *fId );
3729       fToler2 = BRep_Tool::Tolerance( face );
3730       fToler2 *= fToler2 * 10.;
3731       isUPeriodic = surface->IsUPeriodic();
3732       // if ( isUPeriodic )
3733       //   surface->UPeriod();
3734       isVPeriodic = surface->IsVPeriodic();
3735       // if ( isVPeriodic )
3736       //   surface->VPeriod();
3737       surface->Bounds( u1, u2, v1, v2 );
3738       helper.SetSubShape( face );
3739     }
3740     // ---------------------------------------------------------
3741     // for elements on a face, find movable and fixed nodes and
3742     // compute UV for them
3743     // ---------------------------------------------------------
3744     bool checkBoundaryNodes = false;
3745     bool isQuadratic = false;
3746     set<const SMDS_MeshNode*> setMovableNodes;
3747     map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
3748     list< gp_XY > listUV; // uvs the 2 uvMaps refer to
3749     list< const SMDS_MeshElement* > elemsOnFace;
3750
3751     Extrema_GenExtPS projector;
3752     GeomAdaptor_Surface surfAdaptor;
3753     if ( !surface.IsNull() ) {
3754       surfAdaptor.Load( surface );
3755       projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
3756     }
3757     int nbElemOnFace = 0;
3758     itElem = theElems.begin();
3759     // loop on not yet smoothed elements: look for elems on a face
3760     while ( itElem != theElems.end() )
3761     {
3762       if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
3763         break; // all elements found
3764
3765       const SMDS_MeshElement* elem = *itElem;
3766       if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
3767            ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
3768         ++itElem;
3769         continue;
3770       }
3771       elemsOnFace.push_back( elem );
3772       theElems.erase( itElem++ );
3773       nbElemOnFace++;
3774
3775       if ( !isQuadratic )
3776         isQuadratic = elem->IsQuadratic();
3777
3778       // get movable nodes of elem
3779       const SMDS_MeshNode* node;
3780       SMDS_TypeOfPosition posType;
3781       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3782       int nn = 0, nbn =  elem->NbNodes();
3783       if(elem->IsQuadratic())
3784         nbn = nbn/2;
3785       while ( nn++ < nbn ) {
3786         node = static_cast<const SMDS_MeshNode*>( itN->next() );
3787         const SMDS_PositionPtr& pos = node->GetPosition();
3788         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3789         if (posType != SMDS_TOP_EDGE &&
3790             posType != SMDS_TOP_VERTEX &&
3791             theFixedNodes.find( node ) == theFixedNodes.end())
3792         {
3793           // check if all faces around the node are on faceSubMesh
3794           // because a node on edge may be bound to face
3795           bool all = true;
3796           if ( faceSubMesh ) {
3797             SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3798             while ( eIt->more() && all ) {
3799               const SMDS_MeshElement* e = eIt->next();
3800               all = faceSubMesh->Contains( e );
3801             }
3802           }
3803           if ( all )
3804             setMovableNodes.insert( node );
3805           else
3806             checkBoundaryNodes = true;
3807         }
3808         if ( posType == SMDS_TOP_3DSPACE )
3809           checkBoundaryNodes = true;
3810       }
3811
3812       if ( surface.IsNull() )
3813         continue;
3814
3815       // get nodes to check UV
3816       list< const SMDS_MeshNode* > uvCheckNodes;
3817       const SMDS_MeshNode* nodeInFace = 0;
3818       itN = elem->nodesIterator();
3819       nn = 0; nbn =  elem->NbNodes();
3820       if(elem->IsQuadratic())
3821         nbn = nbn/2;
3822       while ( nn++ < nbn ) {
3823         node = static_cast<const SMDS_MeshNode*>( itN->next() );
3824         if ( node->GetPosition()->GetDim() == 2 )
3825           nodeInFace = node;
3826         if ( uvMap.find( node ) == uvMap.end() )
3827           uvCheckNodes.push_back( node );
3828         // add nodes of elems sharing node
3829         //         SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3830         //         while ( eIt->more() ) {
3831         //           const SMDS_MeshElement* e = eIt->next();
3832         //           if ( e != elem ) {
3833         //             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3834         //             while ( nIt->more() ) {
3835         //               const SMDS_MeshNode* n =
3836         //                 static_cast<const SMDS_MeshNode*>( nIt->next() );
3837         //               if ( uvMap.find( n ) == uvMap.end() )
3838         //                 uvCheckNodes.push_back( n );
3839         //             }
3840         //           }
3841         //         }
3842       }
3843       // check UV on face
3844       list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
3845       for ( ; n != uvCheckNodes.end(); ++n ) {
3846         node = *n;
3847         gp_XY uv( 0, 0 );
3848         const SMDS_PositionPtr& pos = node->GetPosition();
3849         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3850         // get existing UV
3851         if ( pos )
3852         {
3853           bool toCheck = true;
3854           uv = helper.GetNodeUV( face, node, nodeInFace, &toCheck );
3855         }
3856         // compute not existing UV
3857         bool project = ( posType == SMDS_TOP_3DSPACE );
3858         // double dist1 = DBL_MAX, dist2 = 0;
3859         // if ( posType != SMDS_TOP_3DSPACE ) {
3860         //   dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
3861         //   project = dist1 > fToler2;
3862         // }
3863         if ( project ) { // compute new UV
3864           gp_XY newUV;
3865           gp_Pnt pNode = SMESH_NodeXYZ( node );
3866           if ( !getClosestUV( projector, pNode, newUV )) {
3867             MESSAGE("Node Projection Failed " << node);
3868           }
3869           else {
3870             if ( isUPeriodic )
3871               newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
3872             if ( isVPeriodic )
3873               newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
3874             // check new UV
3875             // if ( posType != SMDS_TOP_3DSPACE )
3876             //   dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
3877             // if ( dist2 < dist1 )
3878             uv = newUV;
3879           }
3880         }
3881         // store UV in the map
3882         listUV.push_back( uv );
3883         uvMap.insert( make_pair( node, &listUV.back() ));
3884       }
3885     } // loop on not yet smoothed elements
3886
3887     if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
3888       checkBoundaryNodes = true;
3889
3890     // fix nodes on mesh boundary
3891
3892     if ( checkBoundaryNodes ) {
3893       map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
3894       map< SMESH_TLink, int >::iterator link_nb;
3895       // put all elements links to linkNbMap
3896       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3897       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3898         const SMDS_MeshElement* elem = (*elemIt);
3899         int nbn =  elem->NbCornerNodes();
3900         // loop on elem links: insert them in linkNbMap
3901         for ( int iN = 0; iN < nbn; ++iN ) {
3902           const SMDS_MeshNode* n1 = elem->GetNode( iN );
3903           const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
3904           SMESH_TLink link( n1, n2 );
3905           link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
3906           link_nb->second++;
3907         }
3908       }
3909       // remove nodes that are in links encountered only once from setMovableNodes
3910       for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
3911         if ( link_nb->second == 1 ) {
3912           setMovableNodes.erase( link_nb->first.node1() );
3913           setMovableNodes.erase( link_nb->first.node2() );
3914         }
3915       }
3916     }
3917
3918     // -----------------------------------------------------
3919     // for nodes on seam edge, compute one more UV ( uvMap2 );
3920     // find movable nodes linked to nodes on seam and which
3921     // are to be smoothed using the second UV ( uvMap2 )
3922     // -----------------------------------------------------
3923
3924     set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
3925     if ( !surface.IsNull() ) {
3926       TopExp_Explorer eExp( face, TopAbs_EDGE );
3927       for ( ; eExp.More(); eExp.Next() ) {
3928         TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
3929         if ( !BRep_Tool::IsClosed( edge, face ))
3930           continue;
3931         SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
3932         if ( !sm ) continue;
3933         // find out which parameter varies for a node on seam
3934         double f,l;
3935         gp_Pnt2d uv1, uv2;
3936         Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3937         if ( pcurve.IsNull() ) continue;
3938         uv1 = pcurve->Value( f );
3939         edge.Reverse();
3940         pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3941         if ( pcurve.IsNull() ) continue;
3942         uv2 = pcurve->Value( f );
3943         int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
3944         // assure uv1 < uv2
3945         if ( uv1.Coord( iPar ) > uv2.Coord( iPar ))
3946           std::swap( uv1, uv2 );
3947         // get nodes on seam and its vertices
3948         list< const SMDS_MeshNode* > seamNodes;
3949         SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
3950         while ( nSeamIt->more() ) {
3951           const SMDS_MeshNode* node = nSeamIt->next();
3952           if ( !isQuadratic || !IsMedium( node ))
3953             seamNodes.push_back( node );
3954         }
3955         TopExp_Explorer vExp( edge, TopAbs_VERTEX );
3956         for ( ; vExp.More(); vExp.Next() ) {
3957           sm = aMesh->MeshElements( vExp.Current() );
3958           if ( sm ) {
3959             nSeamIt = sm->GetNodes();
3960             while ( nSeamIt->more() )
3961               seamNodes.push_back( nSeamIt->next() );
3962           }
3963         }
3964         // loop on nodes on seam
3965         list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
3966         for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
3967           const SMDS_MeshNode* nSeam = *noSeIt;
3968           map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
3969           if ( n_uv == uvMap.end() )
3970             continue;
3971           // set the first UV
3972           n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
3973           // set the second UV
3974           listUV.push_back( *n_uv->second );
3975           listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
3976           if ( uvMap2.empty() )
3977             uvMap2 = uvMap; // copy the uvMap contents
3978           uvMap2[ nSeam ] = &listUV.back();
3979
3980           // collect movable nodes linked to ones on seam in nodesNearSeam
3981           SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
3982           while ( eIt->more() ) {
3983             const SMDS_MeshElement* e = eIt->next();
3984             int nbUseMap1 = 0, nbUseMap2 = 0;
3985             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3986             int nn = 0, nbn =  e->NbNodes();
3987             if(e->IsQuadratic()) nbn = nbn/2;
3988             while ( nn++ < nbn )
3989             {
3990               const SMDS_MeshNode* n =
3991                 static_cast<const SMDS_MeshNode*>( nIt->next() );
3992               if (n == nSeam ||
3993                   setMovableNodes.find( n ) == setMovableNodes.end() )
3994                 continue;
3995               // add only nodes being closer to uv2 than to uv1
3996               // gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
3997               //              0.5 * ( n->Y() + nSeam->Y() ),
3998               //              0.5 * ( n->Z() + nSeam->Z() ));
3999               // gp_XY uv;
4000               // getClosestUV( projector, pMid, uv );
4001               double x = uvMap[ n ]->Coord( iPar );
4002               if ( Abs( uv1.Coord( iPar ) - x ) >
4003                    Abs( uv2.Coord( iPar ) - x )) {
4004                 nodesNearSeam.insert( n );
4005                 nbUseMap2++;
4006               }
4007               else
4008                 nbUseMap1++;
4009             }
4010             // for centroidalSmooth all element nodes must
4011             // be on one side of a seam
4012             if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
4013               SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4014               nn = 0;
4015               while ( nn++ < nbn ) {
4016                 const SMDS_MeshNode* n =
4017                   static_cast<const SMDS_MeshNode*>( nIt->next() );
4018                 setMovableNodes.erase( n );
4019               }
4020             }
4021           }
4022         } // loop on nodes on seam
4023       } // loop on edge of a face
4024     } // if ( !face.IsNull() )
4025
4026     if ( setMovableNodes.empty() ) {
4027       MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
4028       continue; // goto next face
4029     }
4030
4031     // -------------
4032     // SMOOTHING //
4033     // -------------
4034
4035     int it = -1;
4036     double maxRatio = -1., maxDisplacement = -1.;
4037     set<const SMDS_MeshNode*>::iterator nodeToMove;
4038     for ( it = 0; it < theNbIterations; it++ ) {
4039       maxDisplacement = 0.;
4040       nodeToMove = setMovableNodes.begin();
4041       for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4042         const SMDS_MeshNode* node = (*nodeToMove);
4043         gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
4044
4045         // smooth
4046         bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
4047         if ( theSmoothMethod == LAPLACIAN )
4048           laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
4049         else
4050           centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
4051
4052         // node displacement
4053         gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
4054         Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
4055         if ( aDispl > maxDisplacement )
4056           maxDisplacement = aDispl;
4057       }
4058       // no node movement => exit
4059       //if ( maxDisplacement < 1.e-16 ) {
4060       if ( maxDisplacement < disttol ) {
4061         MESSAGE("-- no node movement --");
4062         break;
4063       }
4064
4065       // check elements quality
4066       maxRatio  = 0;
4067       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4068       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4069         const SMDS_MeshElement* elem = (*elemIt);
4070         if ( !elem || elem->GetType() != SMDSAbs_Face )
4071           continue;
4072         SMESH::Controls::TSequenceOfXYZ aPoints;
4073         if ( aQualityFunc.GetPoints( elem, aPoints )) {
4074           double aValue = aQualityFunc.GetValue( aPoints );
4075           if ( aValue > maxRatio )
4076             maxRatio = aValue;
4077         }
4078       }
4079       if ( maxRatio <= theTgtAspectRatio ) {
4080         //MESSAGE("-- quality achieved --");
4081         break;
4082       }
4083       if (it+1 == theNbIterations) {
4084         //MESSAGE("-- Iteration limit exceeded --");
4085       }
4086     } // smoothing iterations
4087
4088     // MESSAGE(" Face id: " << *fId <<
4089     //         " Nb iterstions: " << it <<
4090     //         " Displacement: " << maxDisplacement <<
4091     //         " Aspect Ratio " << maxRatio);
4092
4093     // ---------------------------------------
4094     // new nodes positions are computed,
4095     // record movement in DS and set new UV
4096     // ---------------------------------------
4097     nodeToMove = setMovableNodes.begin();
4098     for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4099       SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
4100       aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
4101       map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
4102       if ( node_uv != uvMap.end() ) {
4103         gp_XY* uv = node_uv->second;
4104         node->SetPosition
4105           ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
4106       }
4107     }
4108
4109     // move medium nodes of quadratic elements
4110     if ( isQuadratic )
4111     {
4112       vector<const SMDS_MeshNode*> nodes;
4113       bool checkUV;
4114       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4115       for ( ; elemIt != elemsOnFace.end(); ++elemIt )
4116       {
4117         const SMDS_MeshElement* QF = *elemIt;
4118         if ( QF->IsQuadratic() )
4119         {
4120           nodes.assign( SMDS_MeshElement::iterator( QF->interlacedNodesIterator() ),
4121                         SMDS_MeshElement::iterator() );
4122           nodes.push_back( nodes[0] );
4123           gp_Pnt xyz;
4124           for (size_t i = 1; i < nodes.size(); i += 2 ) // i points to a medium node
4125           {
4126             if ( !surface.IsNull() )
4127             {
4128               gp_XY uv1 = helper.GetNodeUV( face, nodes[i-1], nodes[i+1], &checkUV );
4129               gp_XY uv2 = helper.GetNodeUV( face, nodes[i+1], nodes[i-1], &checkUV );
4130               gp_XY uv  = helper.GetMiddleUV( surface, uv1, uv2 );
4131               xyz = surface->Value( uv.X(), uv.Y() );
4132             }
4133             else {
4134               xyz = 0.5 * ( SMESH_NodeXYZ( nodes[i-1] ) + SMESH_NodeXYZ( nodes[i+1] ));
4135             }
4136             if (( SMESH_NodeXYZ( nodes[i] ) - xyz.XYZ() ).Modulus() > disttol )
4137               // we have to move a medium node
4138               aMesh->MoveNode( nodes[i], xyz.X(), xyz.Y(), xyz.Z() );
4139           }
4140         }
4141       }
4142     }
4143
4144   } // loop on face ids
4145
4146 }
4147
4148 namespace
4149 {
4150   //=======================================================================
4151   //function : isReverse
4152   //purpose  : Return true if normal of prevNodes is not co-directied with
4153   //           gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
4154   //           iNotSame is where prevNodes and nextNodes are different.
4155   //           If result is true then future volume orientation is OK
4156   //=======================================================================
4157
4158   bool isReverse(const SMDS_MeshElement*             face,
4159                  const vector<const SMDS_MeshNode*>& prevNodes,
4160                  const vector<const SMDS_MeshNode*>& nextNodes,
4161                  const int                           iNotSame)
4162   {
4163
4164     SMESH_NodeXYZ pP = prevNodes[ iNotSame ];
4165     SMESH_NodeXYZ pN = nextNodes[ iNotSame ];
4166     gp_XYZ extrDir( pN - pP ), faceNorm;
4167     SMESH_MeshAlgos::FaceNormal( face, faceNorm, /*normalized=*/false );
4168
4169     return faceNorm * extrDir < 0.0;
4170   }
4171
4172   //================================================================================
4173   /*!
4174    * \brief Assure that theElemSets[0] holds elements, not nodes
4175    */
4176   //================================================================================
4177
4178   void setElemsFirst( TIDSortedElemSet theElemSets[2] )
4179   {
4180     if ( !theElemSets[0].empty() &&
4181          (*theElemSets[0].begin())->GetType() == SMDSAbs_Node )
4182     {
4183       std::swap( theElemSets[0], theElemSets[1] );
4184     }
4185     else if ( !theElemSets[1].empty() &&
4186               (*theElemSets[1].begin())->GetType() != SMDSAbs_Node )
4187     {
4188       std::swap( theElemSets[0], theElemSets[1] );
4189     }
4190   }
4191 }
4192
4193 //=======================================================================
4194 /*!
4195  * \brief Create elements by sweeping an element
4196  * \param elem - element to sweep
4197  * \param newNodesItVec - nodes generated from each node of the element
4198  * \param newElems - generated elements
4199  * \param nbSteps - number of sweeping steps
4200  * \param srcElements - to append elem for each generated element
4201  */
4202 //=======================================================================
4203
4204 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement*               elem,
4205                                     const vector<TNodeOfNodeListMapItr> & newNodesItVec,
4206                                     list<const SMDS_MeshElement*>&        newElems,
4207                                     const size_t                          nbSteps,
4208                                     SMESH_SequenceOfElemPtr&              srcElements)
4209 {
4210   SMESHDS_Mesh* aMesh = GetMeshDS();
4211
4212   const int           nbNodes = elem->NbNodes();
4213   const int         nbCorners = elem->NbCornerNodes();
4214   SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
4215                                                           polyhedron creation !!! */
4216   // Loop on elem nodes:
4217   // find new nodes and detect same nodes indices
4218   vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
4219   vector<const SMDS_MeshNode*> prevNod( nbNodes );
4220   vector<const SMDS_MeshNode*> nextNod( nbNodes );
4221   vector<const SMDS_MeshNode*> midlNod( nbNodes );
4222
4223   int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
4224   vector<int> sames(nbNodes);
4225   vector<bool> isSingleNode(nbNodes);
4226
4227   for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4228     TNodeOfNodeListMapItr                        nnIt = newNodesItVec[ iNode ];
4229     const SMDS_MeshNode*                         node = nnIt->first;
4230     const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
4231     if ( listNewNodes.empty() )
4232       return;
4233
4234     itNN   [ iNode ] = listNewNodes.begin();
4235     prevNod[ iNode ] = node;
4236     nextNod[ iNode ] = listNewNodes.front();
4237
4238     isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
4239                                                              corner node of linear */
4240     if ( prevNod[ iNode ] != nextNod [ iNode ])
4241       nbDouble += !isSingleNode[iNode];
4242
4243     if( iNode < nbCorners ) { // check corners only
4244       if ( prevNod[ iNode ] == nextNod [ iNode ])
4245         sames[nbSame++] = iNode;
4246       else
4247         iNotSameNode = iNode;
4248     }
4249   }
4250
4251   if ( nbSame == nbNodes || nbSame > 2) {
4252     MESSAGE( " Too many same nodes of element " << elem->GetID() );
4253     return;
4254   }
4255
4256   if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
4257   {
4258     // fix nodes order to have bottom normal external
4259     if ( baseType == SMDSEntity_Polygon )
4260     {
4261       std::reverse( itNN.begin(), itNN.end() );
4262       std::reverse( prevNod.begin(), prevNod.end() );
4263       std::reverse( midlNod.begin(), midlNod.end() );
4264       std::reverse( nextNod.begin(), nextNod.end() );
4265       std::reverse( isSingleNode.begin(), isSingleNode.end() );
4266     }
4267     else
4268     {
4269       const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType, nbNodes );
4270       SMDS_MeshCell::applyInterlace( ind, itNN );
4271       SMDS_MeshCell::applyInterlace( ind, prevNod );
4272       SMDS_MeshCell::applyInterlace( ind, nextNod );
4273       SMDS_MeshCell::applyInterlace( ind, midlNod );
4274       SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4275       if ( nbSame > 0 )
4276       {
4277         sames[nbSame] = iNotSameNode;
4278         for ( int j = 0; j <= nbSame; ++j )
4279           for ( size_t i = 0; i < ind.size(); ++i )
4280             if ( ind[i] == sames[j] )
4281             {
4282               sames[j] = i;
4283               break;
4284             }
4285         iNotSameNode = sames[nbSame];
4286       }
4287     }
4288   }
4289   else if ( elem->GetType() == SMDSAbs_Edge )
4290   {
4291     // orient a new face same as adjacent one
4292     int i1, i2;
4293     const SMDS_MeshElement* e;
4294     TIDSortedElemSet dummy;
4295     if (( e = SMESH_MeshAlgos::FindFaceInSet( nextNod[0], prevNod[0], dummy,dummy, &i1, &i2 )) ||
4296         ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[1], nextNod[1], dummy,dummy, &i1, &i2 )) ||
4297         ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[0], prevNod[1], dummy,dummy, &i1, &i2 )))
4298     {
4299       // there is an adjacent face, check order of nodes in it
4300       bool sameOrder = ( Abs( i2 - i1 ) == 1 ) ? ( i2 > i1 ) : ( i2 < i1 );
4301       if ( sameOrder )
4302       {
4303         std::swap( itNN[0],    itNN[1] );
4304         std::swap( prevNod[0], prevNod[1] );
4305         std::swap( nextNod[0], nextNod[1] );
4306         std::swap( isSingleNode[0], isSingleNode[1] );
4307         if ( nbSame > 0 )
4308           sames[0] = 1 - sames[0];
4309         iNotSameNode = 1 - iNotSameNode;
4310       }
4311     }
4312   }
4313
4314   int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
4315   if ( nbSame > 0 ) {
4316     iSameNode    = sames[ nbSame-1 ];
4317     iBeforeSame  = ( iSameNode + nbCorners - 1 ) % nbCorners;
4318     iAfterSame   = ( iSameNode + 1 ) % nbCorners;
4319     iOpposSame   = ( iSameNode - 2 < 0  ? iSameNode + 2 : iSameNode - 2 );
4320   }
4321
4322   if ( baseType == SMDSEntity_Polygon )
4323   {
4324     if      ( nbNodes == 3 ) baseType = SMDSEntity_Triangle;
4325     else if ( nbNodes == 4 ) baseType = SMDSEntity_Quadrangle;
4326   }
4327   else if ( baseType == SMDSEntity_Quad_Polygon )
4328   {
4329     if      ( nbNodes == 6 ) baseType = SMDSEntity_Quad_Triangle;
4330     else if ( nbNodes == 8 ) baseType = SMDSEntity_Quad_Quadrangle;
4331   }
4332
4333   // make new elements
4334   for ( size_t iStep = 0; iStep < nbSteps; iStep++ )
4335   {
4336     // get next nodes
4337     for ( iNode = 0; iNode < nbNodes; iNode++ )
4338     {
4339       midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
4340       nextNod[ iNode ] = *itNN[ iNode ]++;
4341     }
4342
4343     SMDS_MeshElement* aNewElem = 0;
4344     /*if(!elem->IsPoly())*/ {
4345       switch ( baseType ) {
4346       case SMDSEntity_0D:
4347       case SMDSEntity_Node: { // sweep NODE
4348         if ( nbSame == 0 ) {
4349           if ( isSingleNode[0] )
4350             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
4351           else
4352             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
4353         }
4354         else
4355           return;
4356         break;
4357       }
4358       case SMDSEntity_Edge: { // sweep EDGE
4359         if ( nbDouble == 0 )
4360         {
4361           if ( nbSame == 0 ) // ---> quadrangle
4362             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4363                                       nextNod[ 1 ], nextNod[ 0 ] );
4364           else               // ---> triangle
4365             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4366                                       nextNod[ iNotSameNode ] );
4367         }
4368         else                 // ---> polygon
4369         {
4370           vector<const SMDS_MeshNode*> poly_nodes;
4371           poly_nodes.push_back( prevNod[0] );
4372           poly_nodes.push_back( prevNod[1] );
4373           if ( prevNod[1] != nextNod[1] )
4374           {
4375             if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
4376             poly_nodes.push_back( nextNod[1] );
4377           }
4378           if ( prevNod[0] != nextNod[0] )
4379           {
4380             poly_nodes.push_back( nextNod[0] );
4381             if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
4382           }
4383           switch ( poly_nodes.size() ) {
4384           case 3:
4385             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
4386             break;
4387           case 4:
4388             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
4389                                        poly_nodes[ 2 ], poly_nodes[ 3 ]);
4390             break;
4391           default:
4392             aNewElem = aMesh->AddPolygonalFace (poly_nodes);
4393           }
4394         }
4395         break;
4396       }
4397       case SMDSEntity_Triangle: // TRIANGLE --->
4398       {
4399         if ( nbDouble > 0 ) break;
4400         if ( nbSame == 0 )       // ---> pentahedron
4401           aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4402                                        nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
4403
4404         else if ( nbSame == 1 )  // ---> pyramid
4405           aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4406                                        nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
4407                                        nextNod[ iSameNode ]);
4408
4409         else // 2 same nodes:       ---> tetrahedron
4410           aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4411                                        nextNod[ iNotSameNode ]);
4412         break;
4413       }
4414       case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
4415       {
4416         if ( nbSame == 2 )
4417           return;
4418         if ( nbDouble+nbSame == 2 )
4419         {
4420           if(nbSame==0) {      // ---> quadratic quadrangle
4421             aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4422                                       prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
4423           }
4424           else { //(nbSame==1) // ---> quadratic triangle
4425             if(sames[0]==2) {
4426               return; // medium node on axis
4427             }
4428             else if(sames[0]==0)
4429               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1],
4430                                         prevNod[2], midlNod[1], nextNod[2] );
4431             else // sames[0]==1
4432               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[0],
4433                                         prevNod[2], nextNod[2], midlNod[0]);
4434           }
4435         }
4436         else if ( nbDouble == 3 )
4437         {
4438           if ( nbSame == 0 ) {  // ---> bi-quadratic quadrangle
4439             aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4440                                       prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
4441           }
4442         }
4443         else
4444           return;
4445         break;
4446       }
4447       case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
4448         if ( nbDouble > 0 ) break;
4449
4450         if ( nbSame == 0 )       // ---> hexahedron
4451           aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
4452                                        nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
4453
4454         else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
4455           aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4456                                        nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
4457                                        nextNod[ iSameNode ]);
4458           newElems.push_back( aNewElem );
4459           aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ],  prevNod[ iOpposSame ],
4460                                        prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
4461                                        nextNod[ iOpposSame ],  nextNod[ iBeforeSame ] );
4462         }
4463         else if ( nbSame == 2 ) { // ---> pentahedron
4464           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
4465             // iBeforeSame is same too
4466             aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
4467                                          nextNod[ iOpposSame ],  prevNod[ iSameNode ],
4468                                          prevNod[ iAfterSame ],  nextNod[ iAfterSame ]);
4469           else
4470             // iAfterSame is same too
4471             aNewElem = aMesh->AddVolume (prevNod[ iSameNode ],   prevNod[ iBeforeSame ],
4472                                          nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
4473                                          prevNod[ iOpposSame ],  nextNod[ iOpposSame ]);
4474         }
4475         break;
4476       }
4477       case SMDSEntity_Quad_Triangle:  // sweep (Bi)Quadratic TRIANGLE --->
4478       case SMDSEntity_BiQuad_Triangle: /* ??? */ {
4479         if ( nbDouble+nbSame != 3 ) break;
4480         if(nbSame==0) {
4481           // --->  pentahedron with 15 nodes
4482           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4483                                        nextNod[0], nextNod[1], nextNod[2],
4484                                        prevNod[3], prevNod[4], prevNod[5],
4485                                        nextNod[3], nextNod[4], nextNod[5],
4486                                        midlNod[0], midlNod[1], midlNod[2]);
4487         }
4488         else if(nbSame==1) {
4489           // --->  2d order pyramid of 13 nodes
4490           int apex = iSameNode;
4491           int i0 = ( apex + 1 ) % nbCorners;
4492           int i1 = ( apex - 1 + nbCorners ) % nbCorners;
4493           int i0a = apex + 3;
4494           int i1a = i1 + 3;
4495           int i01 = i0 + 3;
4496           aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
4497                                       nextNod[i0], nextNod[i1], prevNod[apex],
4498                                       prevNod[i01], midlNod[i0],
4499                                       nextNod[i01], midlNod[i1],
4500                                       prevNod[i1a], prevNod[i0a],
4501                                       nextNod[i0a], nextNod[i1a]);
4502         }
4503         else if(nbSame==2) {
4504           // --->  2d order tetrahedron of 10 nodes
4505           int n1 = iNotSameNode;
4506           int n2 = ( n1 + 1             ) % nbCorners;
4507           int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
4508           int n12 = n1 + 3;
4509           int n23 = n2 + 3;
4510           int n31 = n3 + 3;
4511           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
4512                                        prevNod[n12], prevNod[n23], prevNod[n31],
4513                                        midlNod[n1], nextNod[n12], nextNod[n31]);
4514         }
4515         break;
4516       }
4517       case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
4518         if( nbSame == 0 ) {
4519           if ( nbDouble != 4 ) break;
4520           // --->  hexahedron with 20 nodes
4521           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4522                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4523                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4524                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4525                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
4526         }
4527         else if(nbSame==1) {
4528           // ---> pyramid + pentahedron - can not be created since it is needed
4529           // additional middle node at the center of face
4530           //INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
4531           return;
4532         }
4533         else if( nbSame == 2 ) {
4534           if ( nbDouble != 2 ) break;
4535           // --->  2d order Pentahedron with 15 nodes
4536           int n1,n2,n4,n5;
4537           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
4538             // iBeforeSame is same too
4539             n1 = iBeforeSame;
4540             n2 = iOpposSame;
4541             n4 = iSameNode;
4542             n5 = iAfterSame;
4543           }
4544           else {
4545             // iAfterSame is same too
4546             n1 = iSameNode;
4547             n2 = iBeforeSame;
4548             n4 = iAfterSame;
4549             n5 = iOpposSame;
4550           }
4551           int n12 = n2 + 4;
4552           int n45 = n4 + 4;
4553           int n14 = n1 + 4;
4554           int n25 = n5 + 4;
4555           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
4556                                        prevNod[n4], prevNod[n5], nextNod[n5],
4557                                        prevNod[n12], midlNod[n2], nextNod[n12],
4558                                        prevNod[n45], midlNod[n5], nextNod[n45],
4559                                        prevNod[n14], prevNod[n25], nextNod[n25]);
4560         }
4561         break;
4562       }
4563       case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
4564
4565         if( nbSame == 0 && nbDouble == 9 ) {
4566           // --->  tri-quadratic hexahedron with 27 nodes
4567           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4568                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4569                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4570                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4571                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4572                                        prevNod[8], // bottom center
4573                                        midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4574                                        nextNod[8], // top center
4575                                        midlNod[8]);// elem center
4576         }
4577         else
4578         {
4579           return;
4580         }
4581         break;
4582       }
4583       case SMDSEntity_Polygon: { // sweep POLYGON
4584
4585         if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4586           // --->  hexagonal prism
4587           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4588                                        prevNod[3], prevNod[4], prevNod[5],
4589                                        nextNod[0], nextNod[1], nextNod[2],
4590                                        nextNod[3], nextNod[4], nextNod[5]);
4591         }
4592         break;
4593       }
4594       case SMDSEntity_Ball:
4595         return;
4596
4597       default:
4598         break;
4599       } // switch ( baseType )
4600     } // scope
4601
4602     if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4603     {
4604       if ( baseType != SMDSEntity_Polygon )
4605       {
4606         const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType,nbNodes);
4607         SMDS_MeshCell::applyInterlace( ind, prevNod );
4608         SMDS_MeshCell::applyInterlace( ind, nextNod );
4609         SMDS_MeshCell::applyInterlace( ind, midlNod );
4610         SMDS_MeshCell::applyInterlace( ind, itNN );
4611         SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4612         baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4613       }
4614       vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4615       vector<int> quantities (nbNodes + 2);
4616       polyedre_nodes.clear();
4617       quantities.clear();
4618
4619       // bottom of prism
4620       for (int inode = 0; inode < nbNodes; inode++)
4621         polyedre_nodes.push_back( prevNod[inode] );
4622       quantities.push_back( nbNodes );
4623
4624       // top of prism
4625       polyedre_nodes.push_back( nextNod[0] );
4626       for (int inode = nbNodes; inode-1; --inode )
4627         polyedre_nodes.push_back( nextNod[inode-1] );
4628       quantities.push_back( nbNodes );
4629
4630       // side faces
4631       // 3--6--2
4632       // |     |
4633       // 7     5
4634       // |     |
4635       // 0--4--1
4636       const int iQuad = elem->IsQuadratic();
4637       for (int iface = 0; iface < nbNodes; iface += 1+iQuad )
4638       {
4639         const int prevNbNodes = polyedre_nodes.size(); // to detect degenerated face
4640         int inextface = (iface+1+iQuad) % nbNodes;
4641         int imid      = (iface+1) % nbNodes;
4642         polyedre_nodes.push_back( prevNod[inextface] );         // 0
4643         if ( iQuad ) polyedre_nodes.push_back( prevNod[imid] ); // 4
4644         polyedre_nodes.push_back( prevNod[iface] );             // 1
4645         if ( prevNod[iface] != nextNod[iface] ) // 1 != 2
4646         {
4647           if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]); // 5
4648           polyedre_nodes.push_back( nextNod[iface] );                         // 2
4649         }
4650         if ( iQuad ) polyedre_nodes.push_back( nextNod[imid] );               // 6
4651         if ( prevNod[inextface] != nextNod[inextface] ) // 0 != 3
4652         {
4653           polyedre_nodes.push_back( nextNod[inextface] );                            // 3
4654           if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);// 7
4655         }
4656         const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4657         if ( nbFaceNodes > 2 )
4658           quantities.push_back( nbFaceNodes );
4659         else // degenerated face
4660           polyedre_nodes.resize( prevNbNodes );
4661       }
4662       aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4663
4664     } // try to create a polyherdal prism
4665
4666     if ( aNewElem ) {
4667       newElems.push_back( aNewElem );
4668       myLastCreatedElems.push_back(aNewElem);
4669       srcElements.push_back( elem );
4670     }
4671
4672     // set new prev nodes
4673     for ( iNode = 0; iNode < nbNodes; iNode++ )
4674       prevNod[ iNode ] = nextNod[ iNode ];
4675
4676   } // loop on steps
4677 }
4678
4679 //=======================================================================
4680 /*!
4681  * \brief Create 1D and 2D elements around swept elements
4682  * \param mapNewNodes - source nodes and ones generated from them
4683  * \param newElemsMap - source elements and ones generated from them
4684  * \param elemNewNodesMap - nodes generated from each node of each element
4685  * \param elemSet - all swept elements
4686  * \param nbSteps - number of sweeping steps
4687  * \param srcElements - to append elem for each generated element
4688  */
4689 //=======================================================================
4690
4691 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap &     mapNewNodes,
4692                                   TTElemOfElemListMap &    newElemsMap,
4693                                   TElemOfVecOfNnlmiMap &   elemNewNodesMap,
4694                                   TIDSortedElemSet&        elemSet,
4695                                   const int                nbSteps,
4696                                   SMESH_SequenceOfElemPtr& srcElements)
4697 {
4698   ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4699   SMESHDS_Mesh* aMesh = GetMeshDS();
4700
4701   // Find nodes belonging to only one initial element - sweep them into edges.
4702
4703   TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4704   for ( ; nList != mapNewNodes.end(); nList++ )
4705   {
4706     const SMDS_MeshNode* node =
4707       static_cast<const SMDS_MeshNode*>( nList->first );
4708     if ( newElemsMap.count( node ))
4709       continue; // node was extruded into edge
4710     SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4711     int nbInitElems = 0;
4712     const SMDS_MeshElement* el = 0;
4713     SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4714     while ( eIt->more() && nbInitElems < 2 ) {
4715       const SMDS_MeshElement* e = eIt->next();
4716       SMDSAbs_ElementType  type = e->GetType();
4717       if ( type == SMDSAbs_Volume ||
4718            type < highType ||
4719            !elemSet.count(e))
4720         continue;
4721       if ( type > highType ) {
4722         nbInitElems = 0;
4723         highType    = type;
4724       }
4725       el = e;
4726       ++nbInitElems;
4727     }
4728     if ( nbInitElems == 1 ) {
4729       bool NotCreateEdge = el && el->IsMediumNode(node);
4730       if(!NotCreateEdge) {
4731         vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
4732         list<const SMDS_MeshElement*> newEdges;
4733         sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
4734       }
4735     }
4736   }
4737
4738   // Make a ceiling for each element ie an equal element of last new nodes.
4739   // Find free links of faces - make edges and sweep them into faces.
4740
4741   ElemFeatures polyFace( SMDSAbs_Face, /*isPoly=*/true ), anyFace;
4742
4743   TTElemOfElemListMap::iterator  itElem      = newElemsMap.begin();
4744   TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
4745   for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
4746   {
4747     const SMDS_MeshElement* elem = itElem->first;
4748     vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
4749
4750     if(itElem->second.size()==0) continue;
4751
4752     const bool isQuadratic = elem->IsQuadratic();
4753
4754     if ( elem->GetType() == SMDSAbs_Edge ) {
4755       // create a ceiling edge
4756       if ( !isQuadratic ) {
4757         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4758                                vecNewNodes[ 1 ]->second.back())) {
4759           myLastCreatedElems.push_back(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4760                                                       vecNewNodes[ 1 ]->second.back()));
4761           srcElements.push_back( elem );
4762         }
4763       }
4764       else {
4765         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4766                                vecNewNodes[ 1 ]->second.back(),
4767                                vecNewNodes[ 2 ]->second.back())) {
4768           myLastCreatedElems.push_back(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4769                                                       vecNewNodes[ 1 ]->second.back(),
4770                                                       vecNewNodes[ 2 ]->second.back()));
4771           srcElements.push_back( elem );
4772         }
4773       }
4774     }
4775     if ( elem->GetType() != SMDSAbs_Face )
4776       continue;
4777
4778     bool hasFreeLinks = false;
4779
4780     TIDSortedElemSet avoidSet;
4781     avoidSet.insert( elem );
4782
4783     set<const SMDS_MeshNode*> aFaceLastNodes;
4784     int iNode, nbNodes = vecNewNodes.size();
4785     if ( !isQuadratic ) {
4786       // loop on the face nodes
4787       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4788         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4789         // look for free links of the face
4790         int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
4791         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4792         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4793         // check if a link n1-n2 is free
4794         if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
4795           hasFreeLinks = true;
4796           // make a new edge and a ceiling for a new edge
4797           const SMDS_MeshElement* edge;
4798           if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
4799             myLastCreatedElems.push_back( edge = aMesh->AddEdge( n1, n2 )); // free link edge
4800             srcElements.push_back( myLastCreatedElems.back() );
4801           }
4802           n1 = vecNewNodes[ iNode ]->second.back();
4803           n2 = vecNewNodes[ iNext ]->second.back();
4804           if ( !aMesh->FindEdge( n1, n2 )) {
4805             myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2 )); // new edge ceiling
4806             srcElements.push_back( edge );
4807           }
4808         }
4809       }
4810     }
4811     else { // elem is quadratic face
4812       int nbn = nbNodes/2;
4813       for ( iNode = 0; iNode < nbn; iNode++ ) {
4814         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4815         int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
4816         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4817         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4818         const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
4819         // check if a link is free
4820         if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
4821              ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
4822              ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
4823           hasFreeLinks = true;
4824           // make an edge and a ceiling for a new edge
4825           // find medium node
4826           if ( !aMesh->FindEdge( n1, n2, n3 )) {
4827             myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2, n3 )); // free link edge
4828             srcElements.push_back( elem );
4829           }
4830           n1 = vecNewNodes[ iNode ]->second.back();
4831           n2 = vecNewNodes[ iNext ]->second.back();
4832           n3 = vecNewNodes[ iNode+nbn ]->second.back();
4833           if ( !aMesh->FindEdge( n1, n2, n3 )) {
4834             myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
4835             srcElements.push_back( elem );
4836           }
4837         }
4838       }
4839       for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
4840         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4841       }
4842     }
4843
4844     // sweep free links into faces
4845
4846     if ( hasFreeLinks ) {
4847       list<const SMDS_MeshElement*> & newVolumes = itElem->second;
4848       int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
4849
4850       set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
4851       set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
4852       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4853         initNodeSet.insert( vecNewNodes[ iNode ]->first );
4854         topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
4855       }
4856       if ( isQuadratic && nbNodes % 2 ) {  // node set for the case of a biquadratic
4857         initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
4858         initNodeSetNoCenter.erase( vecNewNodes.back()->first );
4859       }
4860       for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
4861         list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
4862         std::advance( v, volNb );
4863         // find indices of free faces of a volume and their source edges
4864         list< int > freeInd;
4865         list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
4866         SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
4867         int iF, nbF = vTool.NbFaces();
4868         for ( iF = 0; iF < nbF; iF ++ ) {
4869           if ( vTool.IsFreeFace( iF ) &&
4870                vTool.GetFaceNodes( iF, faceNodeSet ) &&
4871                initNodeSet != faceNodeSet) // except an initial face
4872           {
4873             if ( nbSteps == 1 && faceNodeSet == topNodeSet )
4874               continue;
4875             if ( faceNodeSet == initNodeSetNoCenter )
4876               continue;
4877             freeInd.push_back( iF );
4878             // find source edge of a free face iF
4879             vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
4880             vector<const SMDS_MeshNode*>::iterator lastCommom;
4881             commonNodes.resize( nbNodes, 0 );
4882             lastCommom = std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
4883                                                 initNodeSet.begin(), initNodeSet.end(),
4884                                                 commonNodes.begin());
4885             if ( std::distance( commonNodes.begin(), lastCommom ) == 3 )
4886               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
4887             else
4888               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
4889 #ifdef _DEBUG_
4890             if ( !srcEdges.back() )
4891             {
4892               cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
4893                    << iF << " of volume #" << vTool.ID() << endl;
4894             }
4895 #endif
4896           }
4897         }
4898         if ( freeInd.empty() )
4899           continue;
4900
4901         // create wall faces for all steps;
4902         // if such a face has been already created by sweep of edge,
4903         // assure that its orientation is OK
4904         for ( int iStep = 0; iStep < nbSteps; iStep++ )
4905         {
4906           vTool.Set( *v, /*ignoreCentralNodes=*/false );
4907           vTool.SetExternalNormal();
4908           const int nextShift = vTool.IsForward() ? +1 : -1;
4909           list< int >::iterator ind = freeInd.begin();
4910           list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
4911           for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
4912           {
4913             const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
4914             int nbn = vTool.NbFaceNodes( *ind );
4915             const SMDS_MeshElement * f = 0;
4916             if ( nbn == 3 )              ///// triangle
4917             {
4918               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
4919               if ( !f ||
4920                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4921               {
4922                 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
4923                                                      nodes[ 1 ],
4924                                                      nodes[ 1 + nextShift ] };
4925                 if ( f )
4926                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4927                 else
4928                   myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4929                                                                newOrder[ 2 ] ));
4930               }
4931             }
4932             else if ( nbn == 4 )       ///// quadrangle
4933             {
4934               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
4935               if ( !f ||
4936                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4937               {
4938                 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
4939                                                      nodes[ 2 ], nodes[ 2+nextShift ] };
4940                 if ( f )
4941                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4942                 else
4943                   myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4944                                                                newOrder[ 2 ], newOrder[ 3 ]));
4945               }
4946             }
4947             else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
4948             {
4949               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
4950               if ( !f ||
4951                    nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
4952               {
4953                 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
4954                                                      nodes[2],
4955                                                      nodes[2 + 2*nextShift],
4956                                                      nodes[3 - 2*nextShift],
4957                                                      nodes[3],
4958                                                      nodes[3 + 2*nextShift]};
4959                 if ( f )
4960                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4961                 else
4962                   myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ],
4963                                                                newOrder[ 1 ],
4964                                                                newOrder[ 2 ],
4965                                                                newOrder[ 3 ],
4966                                                                newOrder[ 4 ],
4967                                                                newOrder[ 5 ] ));
4968               }
4969             }
4970             else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
4971             {
4972               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
4973                                    nodes[1], nodes[3], nodes[5], nodes[7] );
4974               if ( !f ||
4975                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4976               {
4977                 const SMDS_MeshNode* newOrder[8] = { nodes[0],
4978                                                      nodes[4 - 2*nextShift],
4979                                                      nodes[4],
4980                                                      nodes[4 + 2*nextShift],
4981                                                      nodes[1],
4982                                                      nodes[5 - 2*nextShift],
4983                                                      nodes[5],
4984                                                      nodes[5 + 2*nextShift] };
4985                 if ( f )
4986                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4987                 else
4988                   myLastCreatedElems.push_back(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4989                                                               newOrder[ 2 ], newOrder[ 3 ],
4990                                                               newOrder[ 4 ], newOrder[ 5 ],
4991                                                               newOrder[ 6 ], newOrder[ 7 ]));
4992               }
4993             }
4994             else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
4995             {
4996               f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
4997                                       SMDSAbs_Face, /*noMedium=*/false);
4998               if ( !f ||
4999                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5000               {
5001                 const SMDS_MeshNode* newOrder[9] = { nodes[0],
5002                                                      nodes[4 - 2*nextShift],
5003                                                      nodes[4],
5004                                                      nodes[4 + 2*nextShift],
5005                                                      nodes[1],
5006                                                      nodes[5 - 2*nextShift],
5007                                                      nodes[5],
5008                                                      nodes[5 + 2*nextShift],
5009                                                      nodes[8] };
5010                 if ( f )
5011                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5012                 else
5013                   myLastCreatedElems.push_back(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5014                                                               newOrder[ 2 ], newOrder[ 3 ],
5015                                                               newOrder[ 4 ], newOrder[ 5 ],
5016                                                               newOrder[ 6 ], newOrder[ 7 ],
5017                                                               newOrder[ 8 ]));
5018               }
5019             }
5020             else  //////// polygon
5021             {
5022               vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
5023               const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
5024               if ( !f ||
5025                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
5026               {
5027                 if ( !vTool.IsForward() )
5028                   std::reverse( polygon_nodes.begin(), polygon_nodes.end());
5029                 if ( f )
5030                   aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
5031                 else
5032                   AddElement( polygon_nodes, polyFace.SetQuad( (*v)->IsQuadratic() ));
5033               }
5034             }
5035
5036             while ( srcElements.size() < myLastCreatedElems.size() )
5037               srcElements.push_back( *srcEdge );
5038
5039           }  // loop on free faces
5040
5041           // go to the next volume
5042           iVol = 0;
5043           while ( iVol++ < nbVolumesByStep ) v++;
5044
5045         } // loop on steps
5046       } // loop on volumes of one step
5047     } // sweep free links into faces
5048
5049     // Make a ceiling face with a normal external to a volume
5050
5051     // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
5052     SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
5053     int iF = lastVol.GetFaceIndex( aFaceLastNodes );
5054
5055     if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
5056       aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
5057       iF = lastVol.GetFaceIndex( aFaceLastNodes );
5058     }
5059     if ( iF >= 0 )
5060     {
5061       lastVol.SetExternalNormal();
5062       const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
5063       const               int nbn = lastVol.NbFaceNodes( iF );
5064       vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
5065       if ( !hasFreeLinks ||
5066            !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
5067       {
5068         const vector<int>& interlace =
5069           SMDS_MeshCell::interlacedSmdsOrder( elem->GetEntityType(), nbn );
5070         SMDS_MeshCell::applyInterlaceRev( interlace, nodeVec );
5071
5072         AddElement( nodeVec, anyFace.Init( elem ));
5073
5074         while ( srcElements.size() < myLastCreatedElems.size() )
5075           srcElements.push_back( elem );
5076       }
5077     }
5078   } // loop on swept elements
5079 }
5080
5081 //=======================================================================
5082 //function : RotationSweep
5083 //purpose  :
5084 //=======================================================================
5085
5086 SMESH_MeshEditor::PGroupIDs
5087 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet   theElemSets[2],
5088                                 const gp_Ax1&      theAxis,
5089                                 const double       theAngle,
5090                                 const int          theNbSteps,
5091                                 const double       theTol,
5092                                 const bool         theMakeGroups,
5093                                 const bool         theMakeWalls)
5094 {
5095   ClearLastCreated();
5096
5097   setElemsFirst( theElemSets );
5098   myLastCreatedElems.reserve( theElemSets[0].size() * theNbSteps );
5099   myLastCreatedNodes.reserve( theElemSets[1].size() * theNbSteps );
5100
5101   // source elements for each generated one
5102   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5103   srcElems.reserve( theElemSets[0].size() );
5104   srcNodes.reserve( theElemSets[1].size() );
5105
5106   gp_Trsf aTrsf;
5107   aTrsf.SetRotation( theAxis, theAngle );
5108   gp_Trsf aTrsf2;
5109   aTrsf2.SetRotation( theAxis, theAngle/2. );
5110
5111   gp_Lin aLine( theAxis );
5112   double aSqTol = theTol * theTol;
5113
5114   SMESHDS_Mesh* aMesh = GetMeshDS();
5115
5116   TNodeOfNodeListMap mapNewNodes;
5117   TElemOfVecOfNnlmiMap mapElemNewNodes;
5118   TTElemOfElemListMap newElemsMap;
5119
5120   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5121                                      myMesh->NbFaces(ORDER_QUADRATIC) +
5122                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
5123   // loop on theElemSets
5124   TIDSortedElemSet::iterator itElem;
5125   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5126   {
5127     TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5128     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5129       const SMDS_MeshElement* elem = *itElem;
5130       if ( !elem || elem->GetType() == SMDSAbs_Volume )
5131         continue;
5132       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5133       newNodesItVec.reserve( elem->NbNodes() );
5134
5135       // loop on elem nodes
5136       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5137       while ( itN->more() )
5138       {
5139         const SMDS_MeshNode* node = cast2Node( itN->next() );
5140
5141         gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
5142         double coord[3];
5143         aXYZ.Coord( coord[0], coord[1], coord[2] );
5144         bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
5145
5146         // check if a node has been already sweeped
5147         TNodeOfNodeListMapItr nIt =
5148           mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5149         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5150         if ( listNewNodes.empty() )
5151         {
5152           // check if we are to create medium nodes between corner ones
5153           bool needMediumNodes = false;
5154           if ( isQuadraticMesh )
5155           {
5156             SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5157             while (it->more() && !needMediumNodes )
5158             {
5159               const SMDS_MeshElement* invElem = it->next();
5160               if ( invElem != elem && !theElems.count( invElem )) continue;
5161               needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5162               if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5163                 needMediumNodes = true;
5164             }
5165           }
5166
5167           // make new nodes
5168           const SMDS_MeshNode * newNode = node;
5169           for ( int i = 0; i < theNbSteps; i++ ) {
5170             if ( !isOnAxis ) {
5171               if ( needMediumNodes )  // create a medium node
5172               {
5173                 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5174                 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5175                 myLastCreatedNodes.push_back(newNode);
5176                 srcNodes.push_back( node );
5177                 listNewNodes.push_back( newNode );
5178                 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5179               }
5180               else {
5181                 aTrsf.Transforms( coord[0], coord[1], coord[2] );
5182               }
5183               // create a corner node
5184               newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5185               myLastCreatedNodes.push_back(newNode);
5186               srcNodes.push_back( node );
5187               listNewNodes.push_back( newNode );
5188             }
5189             else {
5190               listNewNodes.push_back( newNode );
5191               // if ( needMediumNodes )
5192               //   listNewNodes.push_back( newNode );
5193             }
5194           }
5195         }
5196         newNodesItVec.push_back( nIt );
5197       }
5198       // make new elements
5199       sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
5200     }
5201   }
5202
5203   if ( theMakeWalls )
5204     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], theNbSteps, srcElems );
5205
5206   PGroupIDs newGroupIDs;
5207   if ( theMakeGroups )
5208     newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
5209
5210   return newGroupIDs;
5211 }
5212
5213 //=======================================================================
5214 //function : ExtrusParam
5215 //purpose  : standard construction
5216 //=======================================================================
5217
5218 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Vec&            theStep,
5219                                             const int                theNbSteps,
5220                                             const std::list<double>& theScales,
5221                                             const std::list<double>& theAngles,
5222                                             const gp_XYZ*            theBasePoint,
5223                                             const int                theFlags,
5224                                             const double             theTolerance):
5225   myDir( theStep ),
5226   myBaseP( Precision::Infinite(), 0, 0 ),
5227   myFlags( theFlags ),
5228   myTolerance( theTolerance ),
5229   myElemsToUse( NULL )
5230 {
5231   mySteps = new TColStd_HSequenceOfReal;
5232   const double stepSize = theStep.Magnitude();
5233   for (int i=1; i<=theNbSteps; i++ )
5234     mySteps->Append( stepSize );
5235
5236   if ( !theScales.empty() )
5237   {
5238     if ( IsScaleVariation() && (int)theScales.size() < theNbSteps )
5239       linearScaleVariation( theNbSteps, const_cast< std::list<double>& >( theScales ));
5240
5241     // add medium scales
5242     std::list<double>::const_iterator s2 = theScales.begin(), s1 = s2++;
5243     myScales.reserve( theNbSteps * 2 );
5244     myScales.push_back( 0.5 * ( *s1 + 1. ));
5245     myScales.push_back( *s1 );
5246     for ( ; s2 != theScales.end(); s1 = s2++ )
5247     {
5248       myScales.push_back( 0.5 * ( *s1 + *s2 ));
5249       myScales.push_back( *s2 );
5250     }
5251   }
5252
5253   if ( !theAngles.empty() )
5254   {
5255     std::list<double>& angles = const_cast< std::list<double>& >( theAngles );
5256     if ( IsAngleVariation() && (int)theAngles.size() < theNbSteps )
5257       linearAngleVariation( theNbSteps, angles );
5258
5259     // accumulate angles
5260     double angle = 0;
5261     int nbAngles = 0;
5262     std::list<double>::iterator a1 = angles.begin(), a2;
5263     for ( ; a1 != angles.end(); ++a1, ++nbAngles )
5264     {
5265       angle += *a1;
5266       *a1 = angle;
5267     }
5268     while ( nbAngles++ < theNbSteps )
5269       angles.push_back( angles.back() );
5270
5271     // add medium angles
5272     a2 = angles.begin(), a1 = a2++;
5273     myAngles.push_back( 0.5 * *a1 );
5274     myAngles.push_back( *a1 );
5275     for ( ; a2 != angles.end(); a1 = a2++ )
5276     {
5277       myAngles.push_back( 0.5 * ( *a1 + *a2 ));
5278       myAngles.push_back( *a2 );
5279     }
5280   }
5281
5282   if ( theBasePoint )
5283   {
5284     myBaseP = *theBasePoint;
5285   }
5286
5287   if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5288       ( theTolerance > 0 ))
5289   {
5290     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5291   }
5292   else
5293   {
5294     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5295   }
5296 }
5297
5298 //=======================================================================
5299 //function : ExtrusParam
5300 //purpose  : steps are given explicitly
5301 //=======================================================================
5302
5303 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Dir&                   theDir,
5304                                             Handle(TColStd_HSequenceOfReal) theSteps,
5305                                             const int                       theFlags,
5306                                             const double                    theTolerance):
5307   myDir( theDir ),
5308   mySteps( theSteps ),
5309   myFlags( theFlags ),
5310   myTolerance( theTolerance ),
5311   myElemsToUse( NULL )
5312 {
5313   if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5314       ( theTolerance > 0 ))
5315   {
5316     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5317   }
5318   else
5319   {
5320     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5321   }
5322 }
5323
5324 //=======================================================================
5325 //function : ExtrusParam
5326 //purpose  : for extrusion by normal
5327 //=======================================================================
5328
5329 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const double theStepSize,
5330                                             const int    theNbSteps,
5331                                             const int    theFlags,
5332                                             const int    theDim ):
5333   myDir( 1,0,0 ),
5334   mySteps( new TColStd_HSequenceOfReal ),
5335   myFlags( theFlags ),
5336   myTolerance( 0 ),
5337   myElemsToUse( NULL )
5338 {
5339   for (int i = 0; i < theNbSteps; i++ )
5340     mySteps->Append( theStepSize );
5341
5342   if ( theDim == 1 )
5343   {
5344     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal1D;
5345   }
5346   else
5347   {
5348     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal2D;
5349   }
5350 }
5351
5352 //=======================================================================
5353 //function : ExtrusParam
5354 //purpose  : for extrusion along path
5355 //=======================================================================
5356
5357 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const std::vector< PathPoint >& thePoints,
5358                                             const gp_Pnt*                   theBasePoint,
5359                                             const std::list<double>&        theScales,
5360                                             const bool                      theMakeGroups )
5361   : myBaseP( Precision::Infinite(), 0, 0 ),
5362     myFlags( EXTRUSION_FLAG_BOUNDARY | ( theMakeGroups ? EXTRUSION_FLAG_GROUPS : 0 )),
5363     myPathPoints( thePoints )
5364 {
5365   if ( theBasePoint )
5366   {
5367     myBaseP = theBasePoint->XYZ();
5368   }
5369
5370   if ( !theScales.empty() )
5371   {
5372     // add medium scales
5373     std::list<double>::const_iterator s2 = theScales.begin(), s1 = s2++;
5374     myScales.reserve( thePoints.size() * 2 );
5375     myScales.push_back( 0.5 * ( 1. + *s1 ));
5376     myScales.push_back( *s1 );
5377     for ( ; s2 != theScales.end(); s1 = s2++ )
5378     {
5379       myScales.push_back( 0.5 * ( *s1 + *s2 ));
5380       myScales.push_back( *s2 );
5381     }
5382   }
5383
5384   myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesAlongTrack;
5385 }
5386
5387 //=======================================================================
5388 //function : ExtrusParam::SetElementsToUse
5389 //purpose  : stores elements to use for extrusion by normal, depending on
5390 //           state of EXTRUSION_FLAG_USE_INPUT_ELEMS_ONLY flag;
5391 //           define myBaseP for scaling
5392 //=======================================================================
5393
5394 void SMESH_MeshEditor::ExtrusParam::SetElementsToUse( const TIDSortedElemSet& elems,
5395                                                       const TIDSortedElemSet& nodes )
5396 {
5397   myElemsToUse = ToUseInpElemsOnly() ? & elems : 0;
5398
5399   if ( Precision::IsInfinite( myBaseP.X() )) // myBaseP not defined
5400   {
5401     myBaseP.SetCoord( 0.,0.,0. );
5402     TIDSortedElemSet newNodes;
5403
5404     const TIDSortedElemSet* elemSets[] = { &elems, &nodes };
5405     for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5406     {
5407       const TIDSortedElemSet& elements = *( elemSets[ is2ndSet ]);
5408       TIDSortedElemSet::const_iterator itElem = elements.begin();
5409       for ( ; itElem != elements.end(); itElem++ )
5410       {
5411         const SMDS_MeshElement* elem = *itElem;
5412         SMDS_ElemIteratorPtr     itN = elem->nodesIterator();
5413         while ( itN->more() ) {
5414           const SMDS_MeshElement* node = itN->next();
5415           if ( newNodes.insert( node ).second )
5416             myBaseP += SMESH_NodeXYZ( node );
5417         }
5418       }
5419     }
5420     myBaseP /= newNodes.size();
5421   }
5422 }
5423
5424 //=======================================================================
5425 //function : ExtrusParam::beginStepIter
5426 //purpose  : prepare iteration on steps
5427 //=======================================================================
5428
5429 void SMESH_MeshEditor::ExtrusParam::beginStepIter( bool withMediumNodes )
5430 {
5431   myWithMediumNodes = withMediumNodes;
5432   myNextStep = 1;
5433   myCurSteps.clear();
5434 }
5435 //=======================================================================
5436 //function : ExtrusParam::moreSteps
5437 //purpose  : are there more steps?
5438 //=======================================================================
5439
5440 bool SMESH_MeshEditor::ExtrusParam::moreSteps()
5441 {
5442   return myNextStep <= mySteps->Length() || !myCurSteps.empty();
5443 }
5444 //=======================================================================
5445 //function : ExtrusParam::nextStep
5446 //purpose  : returns the next step
5447 //=======================================================================
5448
5449 double SMESH_MeshEditor::ExtrusParam::nextStep()
5450 {
5451   double res = 0;
5452   if ( !myCurSteps.empty() )
5453   {
5454     res = myCurSteps.back();
5455     myCurSteps.pop_back();
5456   }
5457   else if ( myNextStep <= mySteps->Length() )
5458   {
5459     myCurSteps.push_back( mySteps->Value( myNextStep ));
5460     ++myNextStep;
5461     if ( myWithMediumNodes )
5462     {
5463       myCurSteps.back() /= 2.;
5464       myCurSteps.push_back( myCurSteps.back() );
5465     }
5466     res = nextStep();
5467   }
5468   return res;
5469 }
5470
5471 //=======================================================================
5472 //function : ExtrusParam::makeNodesByDir
5473 //purpose  : create nodes for standard extrusion
5474 //=======================================================================
5475
5476 int SMESH_MeshEditor::ExtrusParam::
5477 makeNodesByDir( SMESHDS_Mesh*                     mesh,
5478                 const SMDS_MeshNode*              srcNode,
5479                 std::list<const SMDS_MeshNode*> & newNodes,
5480                 const bool                        makeMediumNodes)
5481 {
5482   gp_XYZ p = SMESH_NodeXYZ( srcNode );
5483
5484   int nbNodes = 0;
5485   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5486   {
5487     p += myDir.XYZ() * nextStep();
5488     const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5489     newNodes.push_back( newNode );
5490   }
5491
5492   if ( !myScales.empty() || !myAngles.empty() )
5493   {
5494     gp_XYZ  center = myBaseP;
5495     gp_Ax1  ratationAxis( center, myDir );
5496     gp_Trsf rotation;
5497
5498     std::list<const SMDS_MeshNode*>::iterator nIt = newNodes.begin();
5499     size_t i = !makeMediumNodes;
5500     for ( beginStepIter( makeMediumNodes );
5501           moreSteps();
5502           ++nIt, i += 1 + !makeMediumNodes )
5503     {
5504       center += myDir.XYZ() * nextStep();
5505
5506       gp_XYZ xyz = SMESH_NodeXYZ( *nIt );
5507       bool moved = false;
5508       if ( i < myScales.size() )
5509       {
5510         xyz = ( myScales[i] * ( xyz - center )) + center;
5511         moved = true;
5512       }
5513       if ( !myAngles.empty() )
5514       {
5515         rotation.SetRotation( ratationAxis, myAngles[i] );
5516         rotation.Transforms( xyz );
5517         moved = true;
5518       }
5519       if ( moved )
5520         mesh->MoveNode( *nIt, xyz.X(), xyz.Y(), xyz.Z() );
5521       else
5522         break;
5523     }
5524   }
5525   return nbNodes;
5526 }
5527
5528 //=======================================================================
5529 //function : ExtrusParam::makeNodesByDirAndSew
5530 //purpose  : create nodes for standard extrusion with sewing
5531 //=======================================================================
5532
5533 int SMESH_MeshEditor::ExtrusParam::
5534 makeNodesByDirAndSew( SMESHDS_Mesh*                     mesh,
5535                       const SMDS_MeshNode*              srcNode,
5536                       std::list<const SMDS_MeshNode*> & newNodes,
5537                       const bool                        makeMediumNodes)
5538 {
5539   gp_XYZ P1 = SMESH_NodeXYZ( srcNode );
5540
5541   int nbNodes = 0;
5542   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5543   {
5544     P1 += myDir.XYZ() * nextStep();
5545
5546     // try to search in sequence of existing nodes
5547     // if myNodes.size()>0 we 'nave to use given sequence
5548     // else - use all nodes of mesh
5549     const SMDS_MeshNode * node = 0;
5550     if ( myNodes.Length() > 0 )
5551     {
5552       for ( int i = 1; i <= myNodes.Length(); i++ )
5553       {
5554         SMESH_NodeXYZ P2 = myNodes.Value(i);
5555         if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5556         {
5557           node = myNodes.Value(i);
5558           break;
5559         }
5560       }
5561     }
5562     else
5563     {
5564       SMDS_NodeIteratorPtr itn = mesh->nodesIterator();
5565       while(itn->more())
5566       {
5567         SMESH_NodeXYZ P2 = itn->next();
5568         if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5569         {
5570           node = P2._node;
5571           break;
5572         }
5573       }
5574     }
5575
5576     if ( !node )
5577       node = mesh->AddNode( P1.X(), P1.Y(), P1.Z() );
5578
5579     newNodes.push_back( node );
5580
5581   } // loop on steps
5582
5583   return nbNodes;
5584 }
5585
5586 //=======================================================================
5587 //function : ExtrusParam::makeNodesByNormal2D
5588 //purpose  : create nodes for extrusion using normals of faces
5589 //=======================================================================
5590
5591 int SMESH_MeshEditor::ExtrusParam::
5592 makeNodesByNormal2D( SMESHDS_Mesh*                     mesh,
5593                      const SMDS_MeshNode*              srcNode,
5594                      std::list<const SMDS_MeshNode*> & newNodes,
5595                      const bool                        makeMediumNodes)
5596 {
5597   const bool alongAvgNorm = ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL );
5598
5599   gp_XYZ p = SMESH_NodeXYZ( srcNode );
5600
5601   // get normals to faces sharing srcNode
5602   vector< gp_XYZ > norms, baryCenters;
5603   gp_XYZ norm, avgNorm( 0,0,0 );
5604   SMDS_ElemIteratorPtr faceIt = srcNode->GetInverseElementIterator( SMDSAbs_Face );
5605   while ( faceIt->more() )
5606   {
5607     const SMDS_MeshElement* face = faceIt->next();
5608     if ( myElemsToUse && !myElemsToUse->count( face ))
5609       continue;
5610     if ( SMESH_MeshAlgos::FaceNormal( face, norm, /*normalized=*/true ))
5611     {
5612       norms.push_back( norm );
5613       avgNorm += norm;
5614       if ( !alongAvgNorm )
5615       {
5616         gp_XYZ bc(0,0,0);
5617         int nbN = 0;
5618         for ( SMDS_ElemIteratorPtr nIt = face->nodesIterator(); nIt->more(); ++nbN )
5619           bc += SMESH_NodeXYZ( nIt->next() );
5620         baryCenters.push_back( bc / nbN );
5621       }
5622     }
5623   }
5624
5625   if ( norms.empty() ) return 0;
5626
5627   double normSize = avgNorm.Modulus();
5628   if ( normSize < std::numeric_limits<double>::min() )
5629     return 0;
5630
5631   if ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL ) // extrude along avgNorm
5632   {
5633     myDir = avgNorm;
5634     return makeNodesByDir( mesh, srcNode, newNodes, makeMediumNodes );
5635   }
5636
5637   avgNorm /= normSize;
5638
5639   int nbNodes = 0;
5640   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5641   {
5642     gp_XYZ pNew = p;
5643     double stepSize = nextStep();
5644
5645     if ( norms.size() > 1 )
5646     {
5647       for ( size_t iF = 0; iF < norms.size(); ++iF ) // loop on faces
5648       {
5649         // translate plane of a face
5650         baryCenters[ iF ] += norms[ iF ] * stepSize;
5651
5652         // find point of intersection of the face plane located at baryCenters[ iF ]
5653         // and avgNorm located at pNew
5654         double d    = -( norms[ iF ] * baryCenters[ iF ]); // d of plane equation ax+by+cz+d=0
5655         double dot  = ( norms[ iF ] * avgNorm );
5656         if ( dot < std::numeric_limits<double>::min() )
5657           dot = stepSize * 1e-3;
5658         double step = -( norms[ iF ] * pNew + d ) / dot;
5659         pNew += step * avgNorm;
5660       }
5661     }
5662     else
5663     {
5664       pNew += stepSize * avgNorm;
5665     }
5666     p = pNew;
5667
5668     const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5669     newNodes.push_back( newNode );
5670   }
5671   return nbNodes;
5672 }
5673
5674 //=======================================================================
5675 //function : ExtrusParam::makeNodesByNormal1D
5676 //purpose  : create nodes for extrusion using normals of edges
5677 //=======================================================================
5678
5679 int SMESH_MeshEditor::ExtrusParam::
5680 makeNodesByNormal1D( SMESHDS_Mesh*                     /*mesh*/,
5681                      const SMDS_MeshNode*              /*srcNode*/,
5682                      std::list<const SMDS_MeshNode*> & /*newNodes*/,
5683                      const bool                        /*makeMediumNodes*/)
5684 {
5685   throw SALOME_Exception("Extrusion 1D by Normal not implemented");
5686   return 0;
5687 }
5688
5689 //=======================================================================
5690 //function : ExtrusParam::makeNodesAlongTrack
5691 //purpose  : create nodes for extrusion along path
5692 //=======================================================================
5693
5694 int SMESH_MeshEditor::ExtrusParam::
5695 makeNodesAlongTrack( SMESHDS_Mesh*                     mesh,
5696                      const SMDS_MeshNode*              srcNode,
5697                      std::list<const SMDS_MeshNode*> & newNodes,
5698                      const bool                        makeMediumNodes)
5699 {
5700   const Standard_Real aTolAng=1.e-4;
5701
5702   gp_Pnt aV0x = myBaseP;
5703   gp_Pnt aPN0 = SMESH_NodeXYZ( srcNode );
5704
5705   const PathPoint& aPP0 = myPathPoints[0];
5706   gp_Pnt aP0x = aPP0.myPnt;
5707   gp_Dir aDT0x= aPP0.myTgt;
5708
5709   std::vector< gp_Pnt > centers;
5710   centers.reserve( NbSteps() * 2 );
5711
5712   gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
5713
5714   for ( size_t j = 1; j < myPathPoints.size(); ++j )
5715   {
5716     const PathPoint&  aPP  = myPathPoints[j];
5717     const gp_Pnt&     aP1x = aPP.myPnt;
5718     const gp_Dir&    aDT1x = aPP.myTgt;
5719
5720     // Translation
5721     gp_Vec aV01x( aP0x, aP1x );
5722     aTrsf.SetTranslation( aV01x );
5723     gp_Pnt aV1x = aV0x.Transformed( aTrsf );
5724     gp_Pnt aPN1 = aPN0.Transformed( aTrsf );
5725
5726     // rotation 1 [ T1,T0 ]
5727     Standard_Real aAngleT1T0 = -aDT1x.Angle( aDT0x );
5728     if ( fabs( aAngleT1T0 ) > aTolAng )
5729     {
5730       gp_Dir aDT1T0 = aDT1x ^ aDT0x;
5731       aTrsfRotT1T0.SetRotation( gp_Ax1( aV1x, aDT1T0 ), aAngleT1T0 );
5732
5733       aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
5734     }
5735
5736     // rotation 2
5737     if ( aPP.myAngle != 0. )
5738     {
5739       aTrsfRot.SetRotation( gp_Ax1( aV1x, aDT1x ), aPP.myAngle );
5740       aPN1 = aPN1.Transformed( aTrsfRot );
5741     }
5742
5743     // make new node
5744     if ( makeMediumNodes )
5745     {
5746       // create additional node
5747       gp_XYZ midP = 0.5 * ( aPN1.XYZ() + aPN0.XYZ() );
5748       const SMDS_MeshNode* newNode = mesh->AddNode( midP.X(), midP.Y(), midP.Z() );
5749       newNodes.push_back( newNode );
5750
5751     }
5752     const SMDS_MeshNode* newNode = mesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
5753     newNodes.push_back( newNode );
5754
5755     centers.push_back( 0.5 * ( aV0x.XYZ() + aV1x.XYZ() ));
5756     centers.push_back( aV1x );
5757
5758     aPN0 = aPN1;
5759     aP0x = aP1x;
5760     aV0x = aV1x;
5761     aDT0x = aDT1x;
5762   }
5763
5764   // scale
5765   if ( !myScales.empty() )
5766   {
5767     gp_Trsf aTrsfScale;
5768     std::list<const SMDS_MeshNode*>::iterator node = newNodes.begin();
5769     for ( size_t i = !makeMediumNodes;
5770           i < myScales.size() && node != newNodes.end();
5771           i += ( 1 + !makeMediumNodes ), ++node )
5772     {
5773       aTrsfScale.SetScale( centers[ i ], myScales[ i ] );
5774       gp_Pnt aN = SMESH_NodeXYZ( *node );
5775       gp_Pnt aP = aN.Transformed( aTrsfScale );
5776       mesh->MoveNode( *node, aP.X(), aP.Y(), aP.Z() );
5777     }
5778   }
5779
5780   return myPathPoints.size() + makeMediumNodes * ( myPathPoints.size() - 2 );
5781 }
5782
5783 //=======================================================================
5784 //function : ExtrusionSweep
5785 //purpose  :
5786 //=======================================================================
5787
5788 SMESH_MeshEditor::PGroupIDs
5789 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet     theElems[2],
5790                                   const gp_Vec&        theStep,
5791                                   const int            theNbSteps,
5792                                   TTElemOfElemListMap& newElemsMap,
5793                                   const int            theFlags,
5794                                   const double         theTolerance)
5795 {
5796   std::list<double> dummy;
5797   ExtrusParam aParams( theStep, theNbSteps, dummy, dummy, 0,
5798                        theFlags, theTolerance );
5799   return ExtrusionSweep( theElems, aParams, newElemsMap );
5800 }
5801
5802 namespace
5803 {
5804
5805 //=======================================================================
5806 //function : getOriFactor
5807 //purpose  : Return -1 or 1 depending on if order of given nodes corresponds to
5808 //           edge curve orientation
5809 //=======================================================================
5810
5811   double getOriFactor( const TopoDS_Edge&   edge,
5812                        const SMDS_MeshNode* n1,
5813                        const SMDS_MeshNode* n2,
5814                        SMESH_MesherHelper&  helper)
5815   {
5816     double u1 = helper.GetNodeU( edge, n1, n2 );
5817     double u2 = helper.GetNodeU( edge, n2, n1 );
5818     return u1 < u2 ? 1. : -1.;
5819   }
5820 }
5821
5822 //=======================================================================
5823 //function : ExtrusionSweep
5824 //purpose  :
5825 //=======================================================================
5826
5827 SMESH_MeshEditor::PGroupIDs
5828 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet     theElemSets[2],
5829                                   ExtrusParam&         theParams,
5830                                   TTElemOfElemListMap& newElemsMap)
5831 {
5832   ClearLastCreated();
5833
5834   setElemsFirst( theElemSets );
5835   myLastCreatedElems.reserve( theElemSets[0].size() * theParams.NbSteps() );
5836   myLastCreatedNodes.reserve( theElemSets[1].size() * theParams.NbSteps() );
5837
5838   // source elements for each generated one
5839   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5840   srcElems.reserve( theElemSets[0].size() );
5841   srcNodes.reserve( theElemSets[1].size() );
5842
5843   const int nbSteps = theParams.NbSteps();
5844   theParams.SetElementsToUse( theElemSets[0], theElemSets[1] );
5845
5846   TNodeOfNodeListMap   mapNewNodes;
5847   TElemOfVecOfNnlmiMap mapElemNewNodes;
5848
5849   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5850                                      myMesh->NbFaces(ORDER_QUADRATIC) +
5851                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
5852   // loop on theElems
5853   TIDSortedElemSet::iterator itElem;
5854   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5855   {
5856     TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5857     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5858     {
5859       // check element type
5860       const SMDS_MeshElement* elem = *itElem;
5861       if ( !elem  || elem->GetType() == SMDSAbs_Volume )
5862         continue;
5863
5864       const size_t nbNodes = elem->NbNodes();
5865       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5866       newNodesItVec.reserve( nbNodes );
5867
5868       // loop on elem nodes
5869       SMDS_NodeIteratorPtr itN = elem->nodeIterator();
5870       while ( itN->more() )
5871       {
5872         // check if a node has been already sweeped
5873         const SMDS_MeshNode* node = itN->next();
5874         TNodeOfNodeListMap::iterator nIt =
5875           mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5876         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5877         if ( listNewNodes.empty() )
5878         {
5879           // make new nodes
5880
5881           // check if we are to create medium nodes between corner ones
5882           bool needMediumNodes = false;
5883           if ( isQuadraticMesh )
5884           {
5885             SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5886             while (it->more() && !needMediumNodes )
5887             {
5888               const SMDS_MeshElement* invElem = it->next();
5889               if ( invElem != elem && !theElems.count( invElem )) continue;
5890               needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5891               if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5892                 needMediumNodes = true;
5893             }
5894           }
5895           // create nodes for all steps
5896           if ( theParams.MakeNodes( GetMeshDS(), node, listNewNodes, needMediumNodes ))
5897           {
5898             list<const SMDS_MeshNode*>::iterator newNodesIt = listNewNodes.begin();
5899             for ( ; newNodesIt != listNewNodes.end(); ++newNodesIt )
5900             {
5901               myLastCreatedNodes.push_back( *newNodesIt );
5902               srcNodes.push_back( node );
5903             }
5904           }
5905           else
5906           {
5907             if ( theParams.ToMakeBoundary() )
5908             {
5909               GetMeshDS()->Modified();
5910               throw SALOME_Exception( SMESH_Comment("Can't extrude node #") << node->GetID() );
5911             }
5912             break; // newNodesItVec will be shorter than nbNodes
5913           }
5914         }
5915         newNodesItVec.push_back( nIt );
5916       }
5917       // make new elements
5918       if ( newNodesItVec.size() == nbNodes )
5919         sweepElement( elem, newNodesItVec, newElemsMap[elem], nbSteps, srcElems );
5920     }
5921   }
5922
5923   if ( theParams.ToMakeBoundary() ) {
5924     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], nbSteps, srcElems );
5925   }
5926   PGroupIDs newGroupIDs;
5927   if ( theParams.ToMakeGroups() )
5928     newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
5929
5930   return newGroupIDs;
5931 }
5932
5933 //=======================================================================
5934 //function : ExtrusionAlongTrack
5935 //purpose  :
5936 //=======================================================================
5937 SMESH_MeshEditor::Extrusion_Error
5938 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet     theElements[2],
5939                                        SMESH_Mesh*          theTrackMesh,
5940                                        SMDS_ElemIteratorPtr theTrackIterator,
5941                                        const SMDS_MeshNode* theN1,
5942                                        std::list<double>&   theAngles,
5943                                        const bool           theAngleVariation,
5944                                        std::list<double>&   theScales,
5945                                        const bool           theScaleVariation,
5946                                        const gp_Pnt*        theRefPoint,
5947                                        const bool           theMakeGroups)
5948 {
5949   ClearLastCreated();
5950
5951   // 1. Check data
5952   if ( theElements[0].empty() && theElements[1].empty() )
5953     return EXTR_NO_ELEMENTS;
5954
5955   ASSERT( theTrackMesh );
5956   if ( ! theTrackIterator || !theTrackIterator->more() )
5957     return EXTR_NO_ELEMENTS;
5958
5959   // 2. Get ordered nodes
5960   SMESH_MeshAlgos::TElemGroupVector branchEdges;
5961   SMESH_MeshAlgos::TNodeGroupVector branchNods;
5962   SMESH_MeshAlgos::Get1DBranches( theTrackIterator, branchEdges, branchNods, theN1 );
5963   if ( branchEdges.empty() )
5964     return EXTR_PATH_NOT_EDGE;
5965
5966   if ( branchEdges.size() > 1 )
5967     return EXTR_BAD_PATH_SHAPE;
5968
5969   std::vector< const SMDS_MeshNode* >&    pathNodes = branchNods[0];
5970   std::vector< const SMDS_MeshElement* >& pathEdges = branchEdges[0];
5971   if ( pathNodes[0] != theN1 && pathNodes[1] != theN1 )
5972     return EXTR_BAD_STARTING_NODE;
5973
5974   if ( theTrackMesh->NbEdges( ORDER_QUADRATIC ) > 0 )
5975   {
5976     // add medium nodes to pathNodes
5977     std::vector< const SMDS_MeshNode* >    pathNodes2;
5978     std::vector< const SMDS_MeshElement* > pathEdges2;
5979     pathNodes2.reserve( pathNodes.size() * 2 );
5980     pathEdges2.reserve( pathEdges.size() * 2 );
5981     for ( size_t i = 0; i < pathEdges.size(); ++i )
5982     {
5983       pathNodes2.push_back( pathNodes[i] );
5984       pathEdges2.push_back( pathEdges[i] );
5985       if ( pathEdges[i]->IsQuadratic() )
5986       {
5987         pathNodes2.push_back( pathEdges[i]->GetNode(2) );
5988         pathEdges2.push_back( pathEdges[i] );
5989       }
5990     }
5991     pathNodes2.push_back( pathNodes.back() );
5992     pathEdges.swap( pathEdges2 );
5993     pathNodes.swap( pathNodes2 );
5994   }
5995
5996   // 3. Get path data at pathNodes
5997
5998   std::vector< ExtrusParam::PathPoint > points( pathNodes.size() );
5999
6000   if ( theAngleVariation )
6001     linearAngleVariation( points.size()-1, theAngles );
6002   if ( theScaleVariation )
6003     linearScaleVariation( points.size()-1, theScales );
6004
6005   theAngles.push_front( 0 ); // for the 1st point that is not transformed
6006   std::list<double>::iterator angle = theAngles.begin();
6007
6008   SMESHDS_Mesh* pathMeshDS = theTrackMesh->GetMeshDS();
6009
6010   std::map< int, double > edgeID2OriFactor; // orientation of EDGEs
6011   std::map< int, double >::iterator id2factor;
6012   SMESH_MesherHelper pathHelper( *theTrackMesh );
6013   gp_Pnt p; gp_Vec tangent;
6014   const double tol2 = gp::Resolution() * gp::Resolution();
6015
6016   for ( size_t i = 0; i < pathNodes.size(); ++i )
6017   {
6018     ExtrusParam::PathPoint & point = points[ i ];
6019
6020     point.myPnt = SMESH_NodeXYZ( pathNodes[ i ]);
6021
6022     if ( angle != theAngles.end() )
6023       point.myAngle = *angle++;
6024
6025     tangent.SetCoord( 0,0,0 );
6026     const int          shapeID = pathNodes[ i ]->GetShapeID();
6027     const TopoDS_Shape&  shape = pathMeshDS->IndexToShape( shapeID );
6028     TopAbs_ShapeEnum shapeType = shape.IsNull() ? TopAbs_SHAPE : shape.ShapeType();
6029     switch ( shapeType )
6030     {
6031     case TopAbs_EDGE:
6032     {
6033       TopoDS_Edge edge = TopoDS::Edge( shape );
6034       id2factor = edgeID2OriFactor.insert( std::make_pair( shapeID, 0 )).first;
6035       if ( id2factor->second == 0 )
6036       {
6037         if ( i ) id2factor->second = getOriFactor( edge, pathNodes[i-1], pathNodes[i], pathHelper );
6038         else     id2factor->second = getOriFactor( edge, pathNodes[i], pathNodes[i+1], pathHelper );
6039       }
6040       double u = pathHelper.GetNodeU( edge, pathNodes[i] ), u0, u1;
6041       Handle(Geom_Curve) curve = BRep_Tool::Curve( edge, u0, u1 );
6042       curve->D1( u, p, tangent );
6043       tangent *= id2factor->second;
6044       break;
6045     }
6046     case TopAbs_VERTEX:
6047     {
6048       int nbEdges = 0;
6049       PShapeIteratorPtr shapeIt = pathHelper.GetAncestors( shape, *theTrackMesh, TopAbs_EDGE );
6050       while ( const TopoDS_Shape* edgePtr = shapeIt->next() )
6051       {
6052         int edgeID = pathMeshDS->ShapeToIndex( *edgePtr );
6053         for ( int di = -1; di <= 0; ++di )
6054         {
6055           size_t j = i + di;
6056           if ( j < pathEdges.size() && edgeID == pathEdges[ j ]->GetShapeID() )
6057           {
6058             TopoDS_Edge edge = TopoDS::Edge( *edgePtr );
6059             id2factor = edgeID2OriFactor.insert( std::make_pair( edgeID, 0 )).first;
6060             if ( id2factor->second == 0 )
6061             {
6062               if ( j < i )
6063                 id2factor->second = getOriFactor( edge, pathNodes[i-1], pathNodes[i], pathHelper );
6064               else
6065                 id2factor->second = getOriFactor( edge, pathNodes[i], pathNodes[i+1], pathHelper );
6066             }
6067             double u = pathHelper.GetNodeU( edge, pathNodes[i] ), u0, u1;
6068             Handle(Geom_Curve) curve = BRep_Tool::Curve( edge, u0, u1 );
6069             gp_Vec du;
6070             curve->D1( u, p, du );
6071             double size2 = du.SquareMagnitude();
6072             if ( du.SquareMagnitude() > tol2 )
6073             {
6074               tangent += du.Divided( Sqrt( size2 )) * id2factor->second;
6075               nbEdges++;
6076             }
6077             break;
6078           }
6079         }
6080       }
6081       if ( nbEdges > 0 )
6082         break;
6083     }
6084     // fall through
6085     default:
6086     {
6087       for ( int di = -1; di <= 1; di += 2 )
6088       {
6089         size_t j = i + di;
6090         if ( j < pathNodes.size() )
6091         {
6092           gp_Vec dir( point.myPnt, SMESH_NodeXYZ( pathNodes[ j ]));
6093           double size2 = dir.SquareMagnitude();
6094           if ( size2 > tol2 )
6095             tangent += dir.Divided( Sqrt( size2 )) * di;
6096         }
6097       }
6098     }
6099     } // switch ( shapeType )
6100
6101     if ( tangent.SquareMagnitude() < tol2 )
6102       return EXTR_CANT_GET_TANGENT;
6103
6104     point.myTgt = tangent;
6105
6106   } // loop on pathNodes
6107
6108
6109   ExtrusParam nodeMaker( points, theRefPoint, theScales, theMakeGroups );
6110   TTElemOfElemListMap newElemsMap;
6111
6112   ExtrusionSweep( theElements, nodeMaker, newElemsMap );
6113
6114   return EXTR_OK;
6115 }
6116
6117 //=======================================================================
6118 //function : linearAngleVariation
6119 //purpose  : spread values over nbSteps
6120 //=======================================================================
6121
6122 void SMESH_MeshEditor::linearAngleVariation(const int     nbSteps,
6123                                             list<double>& Angles)
6124 {
6125   int nbAngles = Angles.size();
6126   if( nbSteps > nbAngles && nbAngles > 0 )
6127   {
6128     vector<double> theAngles(nbAngles);
6129     theAngles.assign( Angles.begin(), Angles.end() );
6130
6131     list<double> res;
6132     double rAn2St = double( nbAngles ) / double( nbSteps );
6133     double angPrev = 0, angle;
6134     for ( int iSt = 0; iSt < nbSteps; ++iSt )
6135     {
6136       double angCur = rAn2St * ( iSt+1 );
6137       double angCurFloor  = floor( angCur );
6138       double angPrevFloor = floor( angPrev );
6139       if ( angPrevFloor == angCurFloor )
6140         angle = rAn2St * theAngles[ int( angCurFloor ) ];
6141       else {
6142         int iP = int( angPrevFloor );
6143         double angPrevCeil = ceil(angPrev);
6144         angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6145
6146         int iC = int( angCurFloor );
6147         if ( iC < nbAngles )
6148           angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6149
6150         iP = int( angPrevCeil );
6151         while ( iC-- > iP )
6152           angle += theAngles[ iC ];
6153       }
6154       res.push_back(angle);
6155       angPrev = angCur;
6156     }
6157     Angles.swap( res );
6158   }
6159 }
6160
6161 //=======================================================================
6162 //function : linearScaleVariation
6163 //purpose  : spread values over nbSteps 
6164 //=======================================================================
6165
6166 void SMESH_MeshEditor::linearScaleVariation(const int          theNbSteps,
6167                                             std::list<double>& theScales)
6168 {
6169   int nbScales = theScales.size();
6170   std::vector<double> myScales;
6171   myScales.reserve( theNbSteps );
6172   std::list<double>::const_iterator scale = theScales.begin();
6173   double prevScale = 1.0;
6174   for ( int iSc = 1; scale != theScales.end(); ++scale, ++iSc )
6175   {
6176     int      iStep = int( iSc / double( nbScales ) * theNbSteps + 0.5 );
6177     int    stDelta = Max( 1, iStep - myScales.size());
6178     double scDelta = ( *scale - prevScale ) / stDelta;
6179     for ( int iStep = 0; iStep < stDelta; ++iStep )
6180     {
6181       myScales.push_back( prevScale + scDelta );
6182       prevScale = myScales.back();
6183     }
6184     prevScale = *scale;
6185   }
6186   theScales.assign( myScales.begin(), myScales.end() );
6187 }
6188
6189 //================================================================================
6190 /*!
6191  * \brief Move or copy theElements applying theTrsf to their nodes
6192  *  \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6193  *  \param theTrsf - transformation to apply
6194  *  \param theCopy - if true, create translated copies of theElems
6195  *  \param theMakeGroups - if true and theCopy, create translated groups
6196  *  \param theTargetMesh - mesh to copy translated elements into
6197  *  \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6198  */
6199 //================================================================================
6200
6201 SMESH_MeshEditor::PGroupIDs
6202 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6203                              const gp_Trsf&     theTrsf,
6204                              const bool         theCopy,
6205                              const bool         theMakeGroups,
6206                              SMESH_Mesh*        theTargetMesh)
6207 {
6208   ClearLastCreated();
6209   myLastCreatedElems.reserve( theElems.size() );
6210
6211   bool needReverse = false;
6212   string groupPostfix;
6213   switch ( theTrsf.Form() ) {
6214   case gp_PntMirror:
6215     needReverse = true;
6216     groupPostfix = "mirrored";
6217     break;
6218   case gp_Ax1Mirror:
6219     groupPostfix = "mirrored";
6220     break;
6221   case gp_Ax2Mirror:
6222     needReverse = true;
6223     groupPostfix = "mirrored";
6224     break;
6225   case gp_Rotation:
6226     groupPostfix = "rotated";
6227     break;
6228   case gp_Translation:
6229     groupPostfix = "translated";
6230     break;
6231   case gp_Scale:
6232     groupPostfix = "scaled";
6233     break;
6234   case gp_CompoundTrsf: // different scale by axis
6235     groupPostfix = "scaled";
6236     break;
6237   default:
6238     needReverse = false;
6239     groupPostfix = "transformed";
6240   }
6241
6242   SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6243   SMESHDS_Mesh* aMesh    = GetMeshDS();
6244
6245   SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6246   SMESH_MeshEditor* editor = theTargetMesh ? & targetMeshEditor : theCopy ? this : 0;
6247   SMESH_MeshEditor::ElemFeatures elemType;
6248
6249   // map old node to new one
6250   TNodeNodeMap nodeMap;
6251
6252   // elements sharing moved nodes; those of them which have all
6253   // nodes mirrored but are not in theElems are to be reversed
6254   TIDSortedElemSet inverseElemSet;
6255
6256   // source elements for each generated one
6257   SMESH_SequenceOfElemPtr srcElems, srcNodes;
6258
6259   // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6260   TIDSortedElemSet orphanNode;
6261
6262   if ( theElems.empty() ) // transform the whole mesh
6263   {
6264     // add all elements
6265     SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6266     while ( eIt->more() ) theElems.insert( eIt->next() );
6267     // add orphan nodes
6268     SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6269     while ( nIt->more() )
6270     {
6271       const SMDS_MeshNode* node = nIt->next();
6272       if ( node->NbInverseElements() == 0)
6273         orphanNode.insert( node );
6274     }
6275   }
6276
6277   // loop on elements to transform nodes : first orphan nodes then elems
6278   TIDSortedElemSet::iterator itElem;
6279   TIDSortedElemSet *elements[] = { &orphanNode, &theElems };
6280   for (int i=0; i<2; i++)
6281     for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ )
6282     {
6283       const SMDS_MeshElement* elem = *itElem;
6284       if ( !elem )
6285         continue;
6286
6287       // loop on elem nodes
6288       double coord[3];
6289       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6290       while ( itN->more() )
6291       {
6292         const SMDS_MeshNode* node = cast2Node( itN->next() );
6293         // check if a node has been already transformed
6294         pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6295           nodeMap.insert( make_pair ( node, node ));
6296         if ( !n2n_isnew.second )
6297           continue;
6298
6299         node->GetXYZ( coord );
6300         theTrsf.Transforms( coord[0], coord[1], coord[2] );
6301         if ( theTargetMesh ) {
6302           const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6303           n2n_isnew.first->second = newNode;
6304           myLastCreatedNodes.push_back(newNode);
6305           srcNodes.push_back( node );
6306         }
6307         else if ( theCopy ) {
6308           const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6309           n2n_isnew.first->second = newNode;
6310           myLastCreatedNodes.push_back(newNode);
6311           srcNodes.push_back( node );
6312         }
6313         else {
6314           aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6315           // node position on shape becomes invalid
6316           const_cast< SMDS_MeshNode* > ( node )->SetPosition
6317             ( SMDS_SpacePosition::originSpacePosition() );
6318         }
6319
6320         // keep inverse elements
6321         if ( !theCopy && !theTargetMesh && needReverse ) {
6322           SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6323           while ( invElemIt->more() ) {
6324             const SMDS_MeshElement* iel = invElemIt->next();
6325             inverseElemSet.insert( iel );
6326           }
6327         }
6328       }
6329     } // loop on elems in { &orphanNode, &theElems };
6330
6331   // either create new elements or reverse mirrored ones
6332   if ( !theCopy && !needReverse && !theTargetMesh )
6333     return PGroupIDs();
6334
6335   theElems.insert( inverseElemSet.begin(),inverseElemSet.end() );
6336
6337   // Replicate or reverse elements
6338
6339   std::vector<int> iForw;
6340   vector<const SMDS_MeshNode*> nodes;
6341   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6342   {
6343     const SMDS_MeshElement* elem = *itElem;
6344     if ( !elem ) continue;
6345
6346     SMDSAbs_GeometryType geomType = elem->GetGeomType();
6347     size_t               nbNodes  = elem->NbNodes();
6348     if ( geomType == SMDSGeom_NONE ) continue; // node
6349
6350     nodes.resize( nbNodes );
6351
6352     if ( geomType == SMDSGeom_POLYHEDRA )  // ------------------ polyhedral volume
6353     {
6354       const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( elem );
6355       if ( !aPolyedre )
6356         continue;
6357       nodes.clear();
6358       bool allTransformed = true;
6359       int nbFaces = aPolyedre->NbFaces();
6360       for (int iface = 1; iface <= nbFaces && allTransformed; iface++)
6361       {
6362         int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6363         for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++)
6364         {
6365           const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6366           TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6367           if ( nodeMapIt == nodeMap.end() )
6368             allTransformed = false; // not all nodes transformed
6369           else
6370             nodes.push_back((*nodeMapIt).second);
6371         }
6372         if ( needReverse && allTransformed )
6373           std::reverse( nodes.end() - nbFaceNodes, nodes.end() );
6374       }
6375       if ( !allTransformed )
6376         continue; // not all nodes transformed
6377     }
6378     else // ----------------------- the rest element types
6379     {
6380       while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6381       const vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType(), nbNodes );
6382       const vector<int>&    i = needReverse ? iRev : iForw;
6383
6384       // find transformed nodes
6385       size_t iNode = 0;
6386       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6387       while ( itN->more() ) {
6388         const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
6389         TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6390         if ( nodeMapIt == nodeMap.end() )
6391           break; // not all nodes transformed
6392         nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
6393       }
6394       if ( iNode != nbNodes )
6395         continue; // not all nodes transformed
6396     }
6397
6398     if ( editor ) {
6399       // copy in this or a new mesh
6400       if ( editor->AddElement( nodes, elemType.Init( elem, /*basicOnly=*/false )))
6401         srcElems.push_back( elem );
6402     }
6403     else {
6404       // reverse element as it was reversed by transformation
6405       if ( nbNodes > 2 )
6406         aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
6407     }
6408
6409   } // loop on elements
6410
6411   if ( editor && editor != this )
6412     myLastCreatedElems.swap( editor->myLastCreatedElems );
6413
6414   PGroupIDs newGroupIDs;
6415
6416   if ( ( theMakeGroups && theCopy ) ||
6417        ( theMakeGroups && theTargetMesh ) )
6418     newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh, false );
6419
6420   return newGroupIDs;
6421 }
6422
6423 //================================================================================
6424 /*!
6425  * \brief Make an offset mesh from a source 2D mesh
6426  *  \param [in] theElements - source faces
6427  *  \param [in] theValue - offset value
6428  *  \param [out] theTgtMesh - a mesh to add offset elements to
6429  *  \param [in] theMakeGroups - to generate groups
6430  *  \return PGroupIDs - IDs of created groups. NULL means failure
6431  */
6432 //================================================================================
6433
6434 SMESH_MeshEditor::PGroupIDs SMESH_MeshEditor::Offset( TIDSortedElemSet & theElements,
6435                                                       const double       theValue,
6436                                                       SMESH_Mesh*        theTgtMesh,
6437                                                       const bool         theMakeGroups,
6438                                                       const bool         theCopyElements,
6439                                                       const bool         theFixSelfIntersection)
6440 {
6441   SMESHDS_Mesh*    meshDS = GetMeshDS();
6442   SMESHDS_Mesh* tgtMeshDS = theTgtMesh->GetMeshDS();
6443   SMESH_MeshEditor tgtEditor( theTgtMesh );
6444
6445   SMDS_ElemIteratorPtr eIt;
6446   if ( theElements.empty() ) eIt = meshDS->elementsIterator( SMDSAbs_Face );
6447   else                       eIt = SMESHUtils::elemSetIterator( theElements );
6448
6449   SMESH_MeshAlgos::TElemIntPairVec new2OldFaces;
6450   SMESH_MeshAlgos::TNodeIntPairVec new2OldNodes;
6451   std::unique_ptr< SMDS_Mesh > offsetMesh
6452     ( SMESH_MeshAlgos::MakeOffset( eIt, *meshDS, theValue,
6453                                    theFixSelfIntersection,
6454                                    new2OldFaces, new2OldNodes ));
6455   if ( offsetMesh->NbElements() == 0 )
6456     return PGroupIDs(); // MakeOffset() failed
6457
6458
6459   if ( theTgtMesh == myMesh && !theCopyElements )
6460   {
6461     // clear the source elements
6462     if ( theElements.empty() ) eIt = meshDS->elementsIterator( SMDSAbs_Face );
6463     else                       eIt = SMESHUtils::elemSetIterator( theElements );
6464     while ( eIt->more() )
6465       meshDS->RemoveFreeElement( eIt->next(), 0 );
6466   }
6467
6468   // offsetMesh->Modified();
6469   // offsetMesh->CompactMesh(); // make IDs start from 1
6470
6471   // source elements for each generated one
6472   SMESH_SequenceOfElemPtr srcElems, srcNodes;
6473   srcElems.reserve( new2OldFaces.size() );
6474   srcNodes.reserve( new2OldNodes.size() );
6475
6476   ClearLastCreated();
6477   myLastCreatedElems.reserve( new2OldFaces.size() );
6478   myLastCreatedNodes.reserve( new2OldNodes.size() );
6479
6480   // copy offsetMesh to theTgtMesh
6481
6482   int idShift = meshDS->MaxNodeID();
6483   for ( size_t i = 0; i < new2OldNodes.size(); ++i )
6484     if ( const SMDS_MeshNode* n = new2OldNodes[ i ].first )
6485     {
6486 #ifndef _DEBUG_
6487       if ( n->NbInverseElements() > 0 )
6488 #endif
6489       {
6490         const SMDS_MeshNode* n2 =
6491           tgtMeshDS->AddNodeWithID( n->X(), n->Y(), n->Z(), idShift + n->GetID() );
6492         myLastCreatedNodes.push_back( n2 );
6493         srcNodes.push_back( meshDS->FindNode( new2OldNodes[ i ].second ));
6494       }
6495     }
6496
6497   ElemFeatures elemType;
6498   for ( size_t i = 0; i < new2OldFaces.size(); ++i )
6499     if ( const SMDS_MeshElement* f = new2OldFaces[ i ].first )
6500     {
6501       elemType.Init( f );
6502       elemType.myNodes.clear();
6503       for ( SMDS_NodeIteratorPtr nIt = f->nodeIterator(); nIt->more(); )
6504       {
6505         const SMDS_MeshNode* n2 = nIt->next();
6506         elemType.myNodes.push_back( tgtMeshDS->FindNode( idShift + n2->GetID() ));
6507       }
6508       tgtEditor.AddElement( elemType.myNodes, elemType );
6509       srcElems.push_back( meshDS->FindElement( new2OldFaces[ i ].second ));
6510     }
6511
6512   myLastCreatedElems.swap( tgtEditor.myLastCreatedElems );
6513
6514   PGroupIDs newGroupIDs;
6515   if ( theMakeGroups )
6516     newGroupIDs = generateGroups( srcNodes, srcElems, "offset", theTgtMesh, false );
6517   else
6518     newGroupIDs.reset( new std::list< int > );
6519
6520   return newGroupIDs;
6521 }
6522
6523 //=======================================================================
6524 /*!
6525  * \brief Create groups of elements made during transformation
6526  *  \param nodeGens - nodes making corresponding myLastCreatedNodes
6527  *  \param elemGens - elements making corresponding myLastCreatedElems
6528  *  \param postfix - to push_back to names of new groups
6529  *  \param targetMesh - mesh to create groups in
6530  *  \param topPresent - is there are "top" elements that are created by sweeping
6531  */
6532 //=======================================================================
6533
6534 SMESH_MeshEditor::PGroupIDs
6535 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
6536                                  const SMESH_SequenceOfElemPtr& elemGens,
6537                                  const std::string&             postfix,
6538                                  SMESH_Mesh*                    targetMesh,
6539                                  const bool                     topPresent)
6540 {
6541   PGroupIDs newGroupIDs( new list<int> );
6542   SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6543
6544   // Sort existing groups by types and collect their names
6545
6546   // containers to store an old group and generated new ones;
6547   // 1st new group is for result elems of different type than a source one;
6548   // 2nd new group is for same type result elems ("top" group at extrusion)
6549   using boost::tuple;
6550   using boost::make_tuple;
6551   typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
6552   vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
6553   vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
6554   // group names
6555   set< string > groupNames;
6556
6557   SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
6558   if ( !groupIt->more() ) return newGroupIDs;
6559
6560   int newGroupID = mesh->GetGroupIds().back()+1;
6561   while ( groupIt->more() )
6562   {
6563     SMESH_Group * group = groupIt->next();
6564     if ( !group ) continue;
6565     SMESHDS_GroupBase* groupDS = group->GetGroupDS();
6566     if ( !groupDS || groupDS->IsEmpty() ) continue;
6567     groupNames.insert    ( group->GetName() );
6568     groupDS->SetStoreName( group->GetName() );
6569     const SMDSAbs_ElementType type = groupDS->GetType();
6570     SMESHDS_Group* newGroup    = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6571     SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6572     groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
6573     orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
6574   }
6575
6576   // Loop on nodes and elements to add them in new groups
6577
6578   vector< const SMDS_MeshElement* > resultElems;
6579   for ( int isNodes = 0; isNodes < 2; ++isNodes )
6580   {
6581     const SMESH_SequenceOfElemPtr& gens  = isNodes ? nodeGens : elemGens;
6582     const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
6583     if ( gens.size() != elems.size() )
6584       throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
6585
6586     // loop on created elements
6587     for (size_t iElem = 0; iElem < elems.size(); ++iElem )
6588     {
6589       const SMDS_MeshElement* sourceElem = gens[ iElem ];
6590       if ( !sourceElem ) {
6591         MESSAGE("generateGroups(): NULL source element");
6592         continue;
6593       }
6594       list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
6595       if ( groupsOldNew.empty() ) { // no groups of this type at all
6596         while ( iElem+1 < gens.size() && gens[ iElem+1 ] == sourceElem )
6597           ++iElem; // skip all elements made by sourceElem
6598         continue;
6599       }
6600       // collect all elements made by the iElem-th sourceElem
6601       resultElems.clear();
6602       if ( const SMDS_MeshElement* resElem = elems[ iElem ])
6603         if ( resElem != sourceElem )
6604           resultElems.push_back( resElem );
6605       while ( iElem+1 < gens.size() && gens[ iElem+1 ] == sourceElem )
6606         if ( const SMDS_MeshElement* resElem = elems[ ++iElem ])
6607           if ( resElem != sourceElem )
6608             resultElems.push_back( resElem );
6609
6610       const SMDS_MeshElement* topElem = 0;
6611       if ( isNodes ) // there must be a top element
6612       {
6613         topElem = resultElems.back();
6614         resultElems.pop_back();
6615       }
6616       else
6617       {
6618         vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
6619         for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
6620           if ( (*resElemIt)->GetType() == sourceElem->GetType() )
6621           {
6622             topElem = *resElemIt;
6623             *resElemIt = 0; // erase *resElemIt
6624             break;
6625           }
6626       }
6627       // add resultElems to groups originted from ones the sourceElem belongs to
6628       list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
6629       for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
6630       {
6631         SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
6632         if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
6633         {
6634           // fill in a new group
6635           SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
6636           vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
6637           for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
6638             if ( *resElemIt )
6639               newGroup.Add( *resElemIt );
6640
6641           // fill a "top" group
6642           if ( topElem )
6643           {
6644             SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
6645             newTopGroup.Add( topElem );
6646           }
6647         }
6648       }
6649     } // loop on created elements
6650   }// loop on nodes and elements
6651
6652   // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
6653
6654   list<int> topGrouIds;
6655   for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
6656   {
6657     SMESHDS_GroupBase* oldGroupDS =   orderedOldNewGroups[i]->get<0>();
6658     SMESHDS_Group*   newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
6659                                       orderedOldNewGroups[i]->get<2>() };
6660     for ( int is2nd = 0; is2nd < 2; ++is2nd )
6661     {
6662       SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
6663       if ( newGroupDS->IsEmpty() )
6664       {
6665         mesh->GetMeshDS()->RemoveGroup( newGroupDS );
6666       }
6667       else
6668       {
6669         // set group type
6670         newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
6671
6672         // make a name
6673         const bool isTop = ( topPresent &&
6674                              newGroupDS->GetType() == oldGroupDS->GetType() &&
6675                              is2nd );
6676
6677         string name = oldGroupDS->GetStoreName();
6678         { // remove trailing whitespaces (issue 22599)
6679           size_t size = name.size();
6680           while ( size > 1 && isspace( name[ size-1 ]))
6681             --size;
6682           if ( size != name.size() )
6683           {
6684             name.resize( size );
6685             oldGroupDS->SetStoreName( name.c_str() );
6686           }
6687         }
6688         if ( !targetMesh ) {
6689           string suffix = ( isTop ? "top": postfix.c_str() );
6690           name += "_";
6691           name += suffix;
6692           int nb = 1;
6693           while ( !groupNames.insert( name ).second ) // name exists
6694             name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
6695         }
6696         else if ( isTop ) {
6697           name += "_top";
6698         }
6699         newGroupDS->SetStoreName( name.c_str() );
6700
6701         // make a SMESH_Groups
6702         mesh->AddGroup( newGroupDS );
6703         if ( isTop )
6704           topGrouIds.push_back( newGroupDS->GetID() );
6705         else
6706           newGroupIDs->push_back( newGroupDS->GetID() );
6707       }
6708     }
6709   }
6710   newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
6711
6712   return newGroupIDs;
6713 }
6714
6715 //================================================================================
6716 /*!
6717  *  * \brief Return list of group of nodes close to each other within theTolerance
6718  *  *        Search among theNodes or in the whole mesh if theNodes is empty using
6719  *  *        an Octree algorithm
6720  *  \param [in,out] theNodes - the nodes to treat
6721  *  \param [in]     theTolerance - the tolerance
6722  *  \param [out]    theGroupsOfNodes - the result groups of coincident nodes
6723  *  \param [in]     theSeparateCornersAndMedium - if \c true, in quadratic mesh puts 
6724  *         corner and medium nodes in separate groups
6725  */
6726 //================================================================================
6727
6728 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet &   theNodes,
6729                                             const double         theTolerance,
6730                                             TListOfListOfNodes & theGroupsOfNodes,
6731                                             bool                 theSeparateCornersAndMedium)
6732 {
6733   ClearLastCreated();
6734
6735   if ( myMesh->NbEdges  ( ORDER_QUADRATIC ) +
6736        myMesh->NbFaces  ( ORDER_QUADRATIC ) +
6737        myMesh->NbVolumes( ORDER_QUADRATIC ) == 0 )
6738     theSeparateCornersAndMedium = false;
6739
6740   TIDSortedNodeSet& corners = theNodes;
6741   TIDSortedNodeSet  medium;
6742
6743   if ( theNodes.empty() ) // get all nodes in the mesh
6744   {
6745     TIDSortedNodeSet* nodes[2] = { &corners, &medium };
6746     SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator();
6747     if ( theSeparateCornersAndMedium )
6748       while ( nIt->more() )
6749       {
6750         const SMDS_MeshNode* n = nIt->next();
6751         TIDSortedNodeSet* & nodeSet = nodes[ SMESH_MesherHelper::IsMedium( n )];
6752         nodeSet->insert( nodeSet->end(), n );
6753       }
6754     else
6755       while ( nIt->more() )
6756         theNodes.insert( theNodes.end(), nIt->next() );
6757   }
6758   else if ( theSeparateCornersAndMedium ) // separate corners from medium nodes
6759   {
6760     TIDSortedNodeSet::iterator nIt = corners.begin();
6761     while ( nIt != corners.end() )
6762       if ( SMESH_MesherHelper::IsMedium( *nIt ))
6763       {
6764         medium.insert( medium.end(), *nIt );
6765         corners.erase( nIt++ );
6766       }
6767       else
6768       {
6769         ++nIt;
6770       }
6771   }
6772
6773   if ( !corners.empty() )
6774     SMESH_OctreeNode::FindCoincidentNodes ( corners, &theGroupsOfNodes, theTolerance );
6775   if ( !medium.empty() )
6776     SMESH_OctreeNode::FindCoincidentNodes ( medium, &theGroupsOfNodes, theTolerance );
6777 }
6778
6779 //=======================================================================
6780 //function : SimplifyFace
6781 //purpose  : split a chain of nodes into several closed chains
6782 //=======================================================================
6783
6784 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
6785                                     vector<const SMDS_MeshNode *>&       poly_nodes,
6786                                     vector<int>&                         quantities) const
6787 {
6788   int nbNodes = faceNodes.size();
6789   while ( faceNodes[ 0 ] == faceNodes[ nbNodes-1 ] && nbNodes > 2 )
6790     --nbNodes;
6791   if ( nbNodes < 3 )
6792     return 0;
6793   size_t prevNbQuant = quantities.size();
6794
6795   vector< const SMDS_MeshNode* > simpleNodes; simpleNodes.reserve( nbNodes );
6796   map< const SMDS_MeshNode*, int > nodeIndices; // indices within simpleNodes
6797   map< const SMDS_MeshNode*, int >::iterator nInd;
6798
6799   nodeIndices.insert( make_pair( faceNodes[0], 0 ));
6800   simpleNodes.push_back( faceNodes[0] );
6801   for ( int iCur = 1; iCur < nbNodes; iCur++ )
6802   {
6803     if ( faceNodes[ iCur ] != simpleNodes.back() )
6804     {
6805       int index = simpleNodes.size();
6806       nInd = nodeIndices.insert( make_pair( faceNodes[ iCur ], index )).first;
6807       int prevIndex = nInd->second;
6808       if ( prevIndex < index )
6809       {
6810         // a sub-loop found
6811         int loopLen = index - prevIndex;
6812         if ( loopLen > 2 )
6813         {
6814           // store the sub-loop
6815           quantities.push_back( loopLen );
6816           for ( int i = prevIndex; i < index; i++ )
6817             poly_nodes.push_back( simpleNodes[ i ]);
6818         }
6819         simpleNodes.resize( prevIndex+1 );
6820       }
6821       else
6822       {
6823         simpleNodes.push_back( faceNodes[ iCur ]);
6824       }
6825     }
6826   }
6827
6828   if ( simpleNodes.size() > 2 )
6829   {
6830     quantities.push_back( simpleNodes.size() );
6831     poly_nodes.insert ( poly_nodes.end(), simpleNodes.begin(), simpleNodes.end() );
6832   }
6833
6834   return quantities.size() - prevNbQuant;
6835 }
6836
6837 //=======================================================================
6838 //function : MergeNodes
6839 //purpose  : In each group, the cdr of nodes are substituted by the first one
6840 //           in all elements.
6841 //=======================================================================
6842
6843 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes,
6844                                    const bool           theAvoidMakingHoles)
6845 {
6846   ClearLastCreated();
6847
6848   SMESHDS_Mesh* mesh = GetMeshDS();
6849
6850   TNodeNodeMap nodeNodeMap; // node to replace - new node
6851   set<const SMDS_MeshElement*> elems; // all elements with changed nodes
6852   list< int > rmElemIds, rmNodeIds;
6853   vector< ElemFeatures > newElemDefs;
6854
6855   // Fill nodeNodeMap and elems
6856
6857   TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
6858   for ( ; grIt != theGroupsOfNodes.end(); grIt++ )
6859   {
6860     list<const SMDS_MeshNode*>& nodes = *grIt;
6861     list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
6862     const SMDS_MeshNode* nToKeep = *nIt;
6863     for ( ++nIt; nIt != nodes.end(); nIt++ )
6864     {
6865       const SMDS_MeshNode* nToRemove = *nIt;
6866       nodeNodeMap.insert( make_pair( nToRemove, nToKeep ));
6867       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
6868       while ( invElemIt->more() ) {
6869         const SMDS_MeshElement* elem = invElemIt->next();
6870         elems.insert(elem);
6871       }
6872     }
6873   }
6874
6875   // Apply recursive replacements (BUG 0020185)
6876   TNodeNodeMap::iterator nnIt = nodeNodeMap.begin();
6877   for ( ; nnIt != nodeNodeMap.end(); ++nnIt )
6878   {
6879     const SMDS_MeshNode* nToKeep = nnIt->second;
6880     TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( nToKeep );
6881     while ( nnIt_i != nodeNodeMap.end() && nnIt_i->second != nnIt->second )
6882     {
6883       nToKeep = nnIt_i->second;
6884       nnIt->second = nToKeep;
6885       nnIt_i = nodeNodeMap.find( nToKeep );
6886     }
6887   }
6888
6889   if ( theAvoidMakingHoles )
6890   {
6891     // find elements whose topology changes
6892
6893     vector<const SMDS_MeshElement*> pbElems;
6894     set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
6895     for ( ; eIt != elems.end(); ++eIt )
6896     {
6897       const SMDS_MeshElement* elem = *eIt;
6898       SMDS_ElemIteratorPtr     itN = elem->nodesIterator();
6899       while ( itN->more() )
6900       {
6901         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
6902         TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
6903         if ( nnIt != nodeNodeMap.end() && elem->GetNodeIndex( nnIt->second ) >= 0 )
6904         {
6905           // several nodes of elem stick
6906           pbElems.push_back( elem );
6907           break;
6908         }
6909       }
6910     }
6911     // exclude from merge nodes causing spoiling element
6912     for ( size_t iLoop = 0; iLoop < pbElems.size(); ++iLoop ) // avoid infinite cycle
6913     {
6914       bool nodesExcluded = false;
6915       for ( size_t i = 0; i < pbElems.size(); ++i )
6916       {
6917         size_t prevNbMergeNodes = nodeNodeMap.size();
6918         if ( !applyMerge( pbElems[i], newElemDefs, nodeNodeMap, /*noHoles=*/true ) &&
6919              prevNbMergeNodes < nodeNodeMap.size() )
6920           nodesExcluded = true;
6921       }
6922       if ( !nodesExcluded )
6923         break;
6924     }
6925   }
6926
6927   for ( nnIt = nodeNodeMap.begin(); nnIt != nodeNodeMap.end(); ++nnIt )
6928   {
6929     const SMDS_MeshNode* nToRemove = nnIt->first;
6930     const SMDS_MeshNode* nToKeep   = nnIt->second;
6931     if ( nToRemove != nToKeep )
6932     {
6933       rmNodeIds.push_back( nToRemove->GetID() );
6934       AddToSameGroups( nToKeep, nToRemove, mesh );
6935       // set _alwaysComputed to a sub-mesh of VERTEX to enable further mesh computing
6936       // w/o creating node in place of merged ones.
6937       SMDS_PositionPtr pos = nToRemove->GetPosition();
6938       if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
6939         if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
6940           sm->SetIsAlwaysComputed( true );
6941     }
6942   }
6943
6944   // Change element nodes or remove an element
6945
6946   set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
6947   for ( ; eIt != elems.end(); eIt++ )
6948   {
6949     const SMDS_MeshElement* elem = *eIt;
6950     SMESHDS_SubMesh*          sm = mesh->MeshElements( elem->getshapeId() );
6951
6952     bool keepElem = applyMerge( elem, newElemDefs, nodeNodeMap, /*noHoles=*/false );
6953     if ( !keepElem )
6954       rmElemIds.push_back( elem->GetID() );
6955
6956     for ( size_t i = 0; i < newElemDefs.size(); ++i )
6957     {
6958       if ( i > 0 || !mesh->ChangeElementNodes( elem,
6959                                                & newElemDefs[i].myNodes[0],
6960                                                newElemDefs[i].myNodes.size() ))
6961       {
6962         if ( i == 0 )
6963         {
6964           newElemDefs[i].SetID( elem->GetID() );
6965           mesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
6966           if ( !keepElem ) rmElemIds.pop_back();
6967         }
6968         else
6969         {
6970           newElemDefs[i].SetID( -1 );
6971         }
6972         SMDS_MeshElement* newElem = this->AddElement( newElemDefs[i].myNodes, newElemDefs[i] );
6973         if ( sm && newElem )
6974           sm->AddElement( newElem );
6975         if ( elem != newElem )
6976           ReplaceElemInGroups( elem, newElem, mesh );
6977       }
6978     }
6979   }
6980
6981   // Remove bad elements, then equal nodes (order important)
6982   Remove( rmElemIds, /*isNodes=*/false );
6983   Remove( rmNodeIds, /*isNodes=*/true );
6984
6985   return;
6986 }
6987
6988 //=======================================================================
6989 //function : applyMerge
6990 //purpose  : Compute new connectivity of an element after merging nodes
6991 //  \param [in] elems - the element
6992 //  \param [out] newElemDefs - definition(s) of result element(s)
6993 //  \param [inout] nodeNodeMap - nodes to merge
6994 //  \param [in] avoidMakingHoles - if true and and the element becomes invalid
6995 //              after merging (but not degenerated), removes nodes causing
6996 //              the invalidity from \a nodeNodeMap.
6997 //  \return bool - true if the element should be removed
6998 //=======================================================================
6999
7000 bool SMESH_MeshEditor::applyMerge( const SMDS_MeshElement* elem,
7001                                    vector< ElemFeatures >& newElemDefs,
7002                                    TNodeNodeMap&           nodeNodeMap,
7003                                    const bool              avoidMakingHoles )
7004 {
7005   bool toRemove = false; // to remove elem
7006   int nbResElems = 1;    // nb new elements
7007
7008   newElemDefs.resize(nbResElems);
7009   newElemDefs[0].Init( elem );
7010   newElemDefs[0].myNodes.clear();
7011
7012   set<const SMDS_MeshNode*> nodeSet;
7013   vector< const SMDS_MeshNode*>   curNodes;
7014   vector< const SMDS_MeshNode*> & uniqueNodes = newElemDefs[0].myNodes;
7015   vector<int> iRepl;
7016
7017   const        int  nbNodes = elem->NbNodes();
7018   SMDSAbs_EntityType entity = elem->GetEntityType();
7019
7020   curNodes.resize( nbNodes );
7021   uniqueNodes.resize( nbNodes );
7022   iRepl.resize( nbNodes );
7023   int iUnique = 0, iCur = 0, nbRepl = 0;
7024
7025   // Get new seq of nodes
7026
7027   SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7028   while ( itN->more() )
7029   {
7030     const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7031
7032     TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7033     if ( nnIt != nodeNodeMap.end() ) {
7034       n = (*nnIt).second;
7035     }
7036     curNodes[ iCur ] = n;
7037     bool isUnique = nodeSet.insert( n ).second;
7038     if ( isUnique )
7039       uniqueNodes[ iUnique++ ] = n;
7040     else
7041       iRepl[ nbRepl++ ] = iCur;
7042     iCur++;
7043   }
7044
7045   // Analyse element topology after replacement
7046
7047   int nbUniqueNodes = nodeSet.size();
7048   if ( nbNodes != nbUniqueNodes ) // some nodes stick
7049   {
7050     toRemove = true;
7051     nbResElems = 0;
7052
7053     if ( newElemDefs[0].myIsQuad && newElemDefs[0].myType == SMDSAbs_Face && nbNodes > 6 )
7054     {
7055       // if corner nodes stick, remove medium nodes between them from uniqueNodes
7056       int nbCorners = nbNodes / 2;
7057       for ( int iCur = 0; iCur < nbCorners; ++iCur )
7058       {
7059         int iNext = ( iCur + 1 ) % nbCorners;
7060         if ( curNodes[ iCur ] == curNodes[ iNext ] ) // corners stick
7061         {
7062           int iMedium = iCur + nbCorners;
7063           vector< const SMDS_MeshNode* >::iterator i =
7064             std::find( uniqueNodes.begin() + nbCorners - nbRepl,
7065                        uniqueNodes.end(),
7066                        curNodes[ iMedium ]);
7067           if ( i != uniqueNodes.end() )
7068           {
7069             --nbUniqueNodes;
7070             for ( ; i+1 != uniqueNodes.end(); ++i )
7071               *i = *(i+1);
7072           }
7073         }
7074       }
7075     }
7076
7077     switch ( entity )
7078     {
7079     case SMDSEntity_Polygon:
7080     case SMDSEntity_Quad_Polygon: // Polygon
7081     {
7082       ElemFeatures* elemType = & newElemDefs[0];
7083       const bool isQuad = elemType->myIsQuad;
7084       if ( isQuad )
7085         SMDS_MeshCell::applyInterlace // interlace medium and corner nodes
7086           ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon, nbNodes ), curNodes );
7087
7088       // a polygon can divide into several elements
7089       vector<const SMDS_MeshNode *> polygons_nodes;
7090       vector<int> quantities;
7091       nbResElems = SimplifyFace( curNodes, polygons_nodes, quantities );
7092       newElemDefs.resize( nbResElems );
7093       for ( int inode = 0, iface = 0; iface < nbResElems; iface++ )
7094       {
7095         ElemFeatures* elemType = & newElemDefs[iface];
7096         if ( iface ) elemType->Init( elem );
7097
7098         vector<const SMDS_MeshNode *>& face_nodes = elemType->myNodes;
7099         int nbNewNodes = quantities[iface];
7100         face_nodes.assign( polygons_nodes.begin() + inode,
7101                            polygons_nodes.begin() + inode + nbNewNodes );
7102         inode += nbNewNodes;
7103         if ( isQuad ) // check if a result elem is a valid quadratic polygon
7104         {
7105           bool isValid = ( nbNewNodes % 2 == 0 );
7106           for ( int i = 0; i < nbNewNodes && isValid; ++i )
7107             isValid = ( elem->IsMediumNode( face_nodes[i]) == bool( i % 2 ));
7108           elemType->SetQuad( isValid );
7109           if ( isValid ) // put medium nodes after corners
7110             SMDS_MeshCell::applyInterlaceRev
7111               ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon,
7112                                                     nbNewNodes ), face_nodes );
7113         }
7114         elemType->SetPoly(( nbNewNodes / ( elemType->myIsQuad + 1 ) > 4 ));
7115       }
7116       nbUniqueNodes = newElemDefs[0].myNodes.size();
7117       break;
7118     } // Polygon
7119
7120     case SMDSEntity_Polyhedra: // Polyhedral volume
7121     {
7122       if ( nbUniqueNodes >= 4 )
7123       {
7124         // each face has to be analyzed in order to check volume validity
7125         if ( const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( elem ))
7126         {
7127           int nbFaces = aPolyedre->NbFaces();
7128
7129           vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7130           vector<int>                  & quantities = newElemDefs[0].myPolyhedQuantities;
7131           vector<const SMDS_MeshNode *>  faceNodes;
7132           poly_nodes.clear();
7133           quantities.clear();
7134
7135           for (int iface = 1; iface <= nbFaces; iface++)
7136           {
7137             int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7138             faceNodes.resize( nbFaceNodes );
7139             for (int inode = 1; inode <= nbFaceNodes; inode++)
7140             {
7141               const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7142               TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7143               if ( nnIt != nodeNodeMap.end() ) // faceNode sticks
7144                 faceNode = (*nnIt).second;
7145               faceNodes[inode - 1] = faceNode;
7146             }
7147             SimplifyFace(faceNodes, poly_nodes, quantities);
7148           }
7149
7150           if ( quantities.size() > 3 )
7151           {
7152             // TODO: remove coincident faces
7153             nbResElems = 1;
7154             nbUniqueNodes = newElemDefs[0].myNodes.size();
7155           }
7156         }
7157       }
7158     }
7159     break;
7160
7161     // Regular elements
7162     // TODO not all the possible cases are solved. Find something more generic?
7163     case SMDSEntity_Edge: //////// EDGE
7164     case SMDSEntity_Triangle: //// TRIANGLE
7165     case SMDSEntity_Quad_Triangle:
7166     case SMDSEntity_Tetra:
7167     case SMDSEntity_Quad_Tetra: // TETRAHEDRON
7168     {
7169       break;
7170     }
7171     case SMDSEntity_Quad_Edge:
7172     {
7173       break;
7174     }
7175     case SMDSEntity_Quadrangle: //////////////////////////////////// QUADRANGLE
7176     {
7177       if ( nbUniqueNodes < 3 )
7178         toRemove = true;
7179       else if ( nbRepl == 1 && curNodes[ iRepl[0]] == curNodes[( iRepl[0]+2 )%4 ])
7180         toRemove = true; // opposite nodes stick
7181       else
7182         toRemove = false;
7183       break;
7184     }
7185     case SMDSEntity_Quad_Quadrangle: // Quadratic QUADRANGLE
7186     {
7187       //   1    5    2
7188       //    +---+---+
7189       //    |       |
7190       //   4+       +6
7191       //    |       |
7192       //    +---+---+
7193       //   0    7    3
7194       if ( nbUniqueNodes == 6 &&
7195            iRepl[0] < 4       &&
7196            ( nbRepl == 1 || iRepl[1] >= 4 ))
7197       {
7198         toRemove = false;
7199       }
7200       break;
7201     }
7202     case SMDSEntity_BiQuad_Quadrangle: // Bi-Quadratic QUADRANGLE
7203     {
7204       //   1    5    2
7205       //    +---+---+
7206       //    |       |
7207       //   4+  8+   +6
7208       //    |       |
7209       //    +---+---+
7210       //   0    7    3
7211       if ( nbUniqueNodes == 7 &&
7212            iRepl[0] < 4       &&
7213            ( nbRepl == 1 || iRepl[1] != 8 ))
7214       {
7215         toRemove = false;
7216       }
7217       break;
7218     }
7219     case SMDSEntity_Penta: ///////////////////////////////////// PENTAHEDRON
7220     {
7221       if ( nbUniqueNodes == 4 ) {
7222         // ---------------------------------> tetrahedron
7223         if ( curNodes[3] == curNodes[4] &&
7224              curNodes[3] == curNodes[5] ) {
7225           // top nodes stick
7226           toRemove = false;
7227         }
7228         else if ( curNodes[0] == curNodes[1] &&
7229                   curNodes[0] == curNodes[2] ) {
7230           // bottom nodes stick: set a top before
7231           uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7232           uniqueNodes[ 0 ] = curNodes [ 5 ];
7233           uniqueNodes[ 1 ] = curNodes [ 4 ];
7234           uniqueNodes[ 2 ] = curNodes [ 3 ];
7235           toRemove = false;
7236         }
7237         else if (( curNodes[0] == curNodes[3] ) +
7238                  ( curNodes[1] == curNodes[4] ) +
7239                  ( curNodes[2] == curNodes[5] ) == 2 ) {
7240           // a lateral face turns into a line
7241           toRemove = false;
7242         }
7243       }
7244       else if ( nbUniqueNodes == 5 ) {
7245         // PENTAHEDRON --------------------> pyramid
7246         if ( curNodes[0] == curNodes[3] )
7247         {
7248           uniqueNodes[ 0 ] = curNodes[ 1 ];
7249           uniqueNodes[ 1 ] = curNodes[ 4 ];
7250           uniqueNodes[ 2 ] = curNodes[ 5 ];
7251           uniqueNodes[ 3 ] = curNodes[ 2 ];
7252           uniqueNodes[ 4 ] = curNodes[ 0 ];
7253           toRemove = false;
7254         }
7255         if ( curNodes[1] == curNodes[4] )
7256         {
7257           uniqueNodes[ 0 ] = curNodes[ 0 ];
7258           uniqueNodes[ 1 ] = curNodes[ 2 ];
7259           uniqueNodes[ 2 ] = curNodes[ 5 ];
7260           uniqueNodes[ 3 ] = curNodes[ 3 ];
7261           uniqueNodes[ 4 ] = curNodes[ 1 ];
7262           toRemove = false;
7263         }
7264         if ( curNodes[2] == curNodes[5] )
7265         {
7266           uniqueNodes[ 0 ] = curNodes[ 0 ];
7267           uniqueNodes[ 1 ] = curNodes[ 3 ];
7268           uniqueNodes[ 2 ] = curNodes[ 4 ];
7269           uniqueNodes[ 3 ] = curNodes[ 1 ];
7270           uniqueNodes[ 4 ] = curNodes[ 2 ];
7271           toRemove = false;
7272         }
7273       }
7274       break;
7275     }
7276     case SMDSEntity_Hexa:
7277     {
7278       //////////////////////////////////// HEXAHEDRON
7279       SMDS_VolumeTool hexa (elem);
7280       hexa.SetExternalNormal();
7281       if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7282         //////////////////////// HEX ---> tetrahedron
7283         for ( int iFace = 0; iFace < 6; iFace++ ) {
7284           const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7285           if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7286               curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7287               curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7288             // one face turns into a point ...
7289             int  pickInd = ind[ 0 ];
7290             int iOppFace = hexa.GetOppFaceIndex( iFace );
7291             ind = hexa.GetFaceNodesIndices( iOppFace );
7292             int nbStick = 0;
7293             uniqueNodes.clear();
7294             for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7295               if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7296                 nbStick++;
7297               else
7298                 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7299             }
7300             if ( nbStick == 1 ) {
7301               // ... and the opposite one - into a triangle.
7302               // set a top node
7303               uniqueNodes.push_back( curNodes[ pickInd ]);
7304               toRemove = false;
7305             }
7306             break;
7307           }
7308         }
7309       }
7310       else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7311         //////////////////////// HEX ---> prism
7312         int nbTria = 0, iTria[3];
7313         const int *ind; // indices of face nodes
7314         // look for triangular faces
7315         for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7316           ind = hexa.GetFaceNodesIndices( iFace );
7317           TIDSortedNodeSet faceNodes;
7318           for ( iCur = 0; iCur < 4; iCur++ )
7319             faceNodes.insert( curNodes[ind[iCur]] );
7320           if ( faceNodes.size() == 3 )
7321             iTria[ nbTria++ ] = iFace;
7322         }
7323         // check if triangles are opposite
7324         if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7325         {
7326           // set nodes of the bottom triangle
7327           ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7328           vector<int> indB;
7329           for ( iCur = 0; iCur < 4; iCur++ )
7330             if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7331               indB.push_back( ind[iCur] );
7332           if ( !hexa.IsForward() )
7333             std::swap( indB[0], indB[2] );
7334           for ( iCur = 0; iCur < 3; iCur++ )
7335             uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7336           // set nodes of the top triangle
7337           const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7338           for ( iCur = 0; iCur < 3; ++iCur )
7339             for ( int j = 0; j < 4; ++j )
7340               if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7341               {
7342                 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7343                 break;
7344               }
7345           toRemove = false;
7346           break;
7347         }
7348       }
7349       else if (nbUniqueNodes == 5 && nbRepl == 3 ) {
7350         //////////////////// HEXAHEDRON ---> pyramid
7351         for ( int iFace = 0; iFace < 6; iFace++ ) {
7352           const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7353           if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7354               curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7355               curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7356             // one face turns into a point ...
7357             int iOppFace = hexa.GetOppFaceIndex( iFace );
7358             ind = hexa.GetFaceNodesIndices( iOppFace );
7359             uniqueNodes.clear();
7360             for ( iCur = 0; iCur < 4; iCur++ ) {
7361               if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7362                 break;
7363               else
7364                 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7365             }
7366             if ( uniqueNodes.size() == 4 ) {
7367               // ... and the opposite one is a quadrangle
7368               // set a top node
7369               const int* indTop = hexa.GetFaceNodesIndices( iFace );
7370               uniqueNodes.push_back( curNodes[indTop[ 0 ]]);
7371               toRemove = false;
7372             }
7373             break;
7374           }
7375         }
7376       }
7377
7378       if ( toRemove && nbUniqueNodes > 4 ) {
7379         ////////////////// HEXAHEDRON ---> polyhedron
7380         hexa.SetExternalNormal();
7381         vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7382         vector<int>                  & quantities = newElemDefs[0].myPolyhedQuantities;
7383         poly_nodes.reserve( 6 * 4 ); poly_nodes.clear();
7384         quantities.reserve( 6 );     quantities.clear();
7385         for ( int iFace = 0; iFace < 6; iFace++ )
7386         {
7387           const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7388           if ( curNodes[ind[0]] == curNodes[ind[2]] ||
7389                curNodes[ind[1]] == curNodes[ind[3]] )
7390           {
7391             quantities.clear();
7392             break; // opposite nodes stick
7393           }
7394           nodeSet.clear();
7395           for ( iCur = 0; iCur < 4; iCur++ )
7396           {
7397             if ( nodeSet.insert( curNodes[ind[ iCur ]] ).second )
7398               poly_nodes.push_back( curNodes[ind[ iCur ]]);
7399           }
7400           if ( nodeSet.size() < 3 )
7401             poly_nodes.resize( poly_nodes.size() - nodeSet.size() );
7402           else
7403             quantities.push_back( nodeSet.size() );
7404         }
7405         if ( quantities.size() >= 4 )
7406         {
7407           nbResElems = 1;
7408           nbUniqueNodes = poly_nodes.size();
7409           newElemDefs[0].SetPoly(true);
7410         }
7411       }
7412       break;
7413     } // case HEXAHEDRON
7414
7415     default:
7416       toRemove = true;
7417
7418     } // switch ( entity )
7419
7420     if ( toRemove && nbResElems == 0 && avoidMakingHoles )
7421     {
7422       // erase from nodeNodeMap nodes whose merge spoils elem
7423       vector< const SMDS_MeshNode* > noMergeNodes;
7424       SMESH_MeshAlgos::DeMerge( elem, curNodes, noMergeNodes );
7425       for ( size_t i = 0; i < noMergeNodes.size(); ++i )
7426         nodeNodeMap.erase( noMergeNodes[i] );
7427     }
7428     
7429   } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7430
7431   uniqueNodes.resize( nbUniqueNodes );
7432
7433   if ( !toRemove && nbResElems == 0 )
7434     nbResElems = 1;
7435
7436   newElemDefs.resize( nbResElems );
7437
7438   return !toRemove;
7439 }
7440
7441
7442 // ========================================================
7443 // class   : ComparableElement
7444 // purpose : allow comparing elements basing on their nodes
7445 // ========================================================
7446
7447 class ComparableElement : public boost::container::flat_set< int >
7448 {
7449   typedef boost::container::flat_set< int >  int_set;
7450
7451   const SMDS_MeshElement* myElem;
7452   int                     mySumID;
7453   mutable int             myGroupID;
7454
7455 public:
7456
7457   ComparableElement( const SMDS_MeshElement* theElem ):
7458     myElem ( theElem ), mySumID( 0 ), myGroupID( -1 )
7459   {
7460     this->reserve( theElem->NbNodes() );
7461     for ( SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator(); nodeIt->more(); )
7462     {
7463       int id = nodeIt->next()->GetID();
7464       mySumID += id;
7465       this->insert( id );
7466     }
7467   }
7468
7469   const SMDS_MeshElement* GetElem() const { return myElem; }
7470
7471   int& GroupID() const { return myGroupID; }
7472   //int& GroupID() const { return const_cast< int& >( myGroupID ); }
7473
7474   ComparableElement( const ComparableElement& theSource ) // move copy
7475     : int_set()
7476   {
7477     ComparableElement& src = const_cast< ComparableElement& >( theSource );
7478     (int_set&) (*this ) = std::move( src );
7479     myElem    = src.myElem;
7480     mySumID   = src.mySumID;
7481     myGroupID = src.myGroupID;
7482   }
7483
7484   static int HashCode(const ComparableElement& se, int limit )
7485   {
7486     return ::HashCode( se.mySumID, limit );
7487   }
7488   static Standard_Boolean IsEqual(const ComparableElement& se1, const ComparableElement& se2 )
7489   {
7490     return ( se1 == se2 );
7491   }
7492
7493 };
7494
7495 //=======================================================================
7496 //function : FindEqualElements
7497 //purpose  : Return list of group of elements built on the same nodes.
7498 //           Search among theElements or in the whole mesh if theElements is empty
7499 //=======================================================================
7500
7501 void SMESH_MeshEditor::FindEqualElements( TIDSortedElemSet &        theElements,
7502                                           TListOfListOfElementsID & theGroupsOfElementsID )
7503 {
7504   ClearLastCreated();
7505
7506   SMDS_ElemIteratorPtr elemIt;
7507   if ( theElements.empty() ) elemIt = GetMeshDS()->elementsIterator();
7508   else                       elemIt = SMESHUtils::elemSetIterator( theElements );
7509
7510   typedef NCollection_Map< ComparableElement, ComparableElement > TMapOfElements;
7511   typedef std::list<int>                                          TGroupOfElems;
7512   TMapOfElements               mapOfElements;
7513   std::vector< TGroupOfElems > arrayOfGroups;
7514   TGroupOfElems                groupOfElems;
7515
7516   while ( elemIt->more() )
7517   {
7518     const SMDS_MeshElement* curElem = elemIt->next();
7519     if ( curElem->IsNull() )
7520       continue;
7521     ComparableElement      compElem = curElem;
7522     // check uniqueness
7523     const ComparableElement& elemInSet = mapOfElements.Added( compElem );
7524     if ( elemInSet.GetElem() != curElem ) // coincident elem
7525     {
7526       int& iG = elemInSet.GroupID();
7527       if ( iG < 0 )
7528       {
7529         iG = arrayOfGroups.size();
7530         arrayOfGroups.push_back( groupOfElems );
7531         arrayOfGroups[ iG ].push_back( elemInSet.GetElem()->GetID() );
7532       }
7533       arrayOfGroups[ iG ].push_back( curElem->GetID() );
7534     }
7535   }
7536
7537   groupOfElems.clear();
7538   std::vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
7539   for ( ; groupIt != arrayOfGroups.end(); ++groupIt )
7540   {
7541     if ( groupIt->size() > 1 ) {
7542       //groupOfElems.sort(); -- theElements are sorted already
7543       theGroupsOfElementsID.emplace_back( *groupIt );
7544     }
7545   }
7546 }
7547
7548 //=======================================================================
7549 //function : MergeElements
7550 //purpose  : In each given group, substitute all elements by the first one.
7551 //=======================================================================
7552
7553 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
7554 {
7555   ClearLastCreated();
7556
7557   typedef list<int> TListOfIDs;
7558   TListOfIDs rmElemIds; // IDs of elems to remove
7559
7560   SMESHDS_Mesh* aMesh = GetMeshDS();
7561
7562   TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
7563   while ( groupsIt != theGroupsOfElementsID.end() ) {
7564     TListOfIDs& aGroupOfElemID = *groupsIt;
7565     aGroupOfElemID.sort();
7566     int elemIDToKeep = aGroupOfElemID.front();
7567     const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
7568     aGroupOfElemID.pop_front();
7569     TListOfIDs::iterator idIt = aGroupOfElemID.begin();
7570     while ( idIt != aGroupOfElemID.end() ) {
7571       int elemIDToRemove = *idIt;
7572       const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
7573       // add the kept element in groups of removed one (PAL15188)
7574       AddToSameGroups( elemToKeep, elemToRemove, aMesh );
7575       rmElemIds.push_back( elemIDToRemove );
7576       ++idIt;
7577     }
7578     ++groupsIt;
7579   }
7580
7581   Remove( rmElemIds, false );
7582 }
7583
7584 //=======================================================================
7585 //function : MergeEqualElements
7586 //purpose  : Remove all but one of elements built on the same nodes.
7587 //=======================================================================
7588
7589 void SMESH_MeshEditor::MergeEqualElements()
7590 {
7591   TIDSortedElemSet aMeshElements; /* empty input ==
7592                                      to merge equal elements in the whole mesh */
7593   TListOfListOfElementsID aGroupsOfElementsID;
7594   FindEqualElements( aMeshElements, aGroupsOfElementsID );
7595   MergeElements( aGroupsOfElementsID );
7596 }
7597
7598 //=======================================================================
7599 //function : findAdjacentFace
7600 //purpose  :
7601 //=======================================================================
7602
7603 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
7604                                                 const SMDS_MeshNode* n2,
7605                                                 const SMDS_MeshElement* elem)
7606 {
7607   TIDSortedElemSet elemSet, avoidSet;
7608   if ( elem )
7609     avoidSet.insert ( elem );
7610   return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
7611 }
7612
7613 //=======================================================================
7614 //function : findSegment
7615 //purpose  : Return a mesh segment by two nodes one of which can be medium
7616 //=======================================================================
7617
7618 static const SMDS_MeshElement* findSegment(const SMDS_MeshNode* n1,
7619                                            const SMDS_MeshNode* n2)
7620 {
7621   SMDS_ElemIteratorPtr it = n1->GetInverseElementIterator( SMDSAbs_Edge );
7622   while ( it->more() )
7623   {
7624     const SMDS_MeshElement* seg = it->next();
7625     if ( seg->GetNodeIndex( n2 ) >= 0 )
7626       return seg;
7627   }
7628   return 0;
7629 }
7630
7631 //=======================================================================
7632 //function : FindFreeBorder
7633 //purpose  :
7634 //=======================================================================
7635
7636 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
7637
7638 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode*             theFirstNode,
7639                                        const SMDS_MeshNode*             theSecondNode,
7640                                        const SMDS_MeshNode*             theLastNode,
7641                                        list< const SMDS_MeshNode* > &   theNodes,
7642                                        list< const SMDS_MeshElement* >& theFaces)
7643 {
7644   if ( !theFirstNode || !theSecondNode )
7645     return false;
7646   // find border face between theFirstNode and theSecondNode
7647   const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
7648   if ( !curElem )
7649     return false;
7650
7651   theFaces.push_back( curElem );
7652   theNodes.push_back( theFirstNode );
7653   theNodes.push_back( theSecondNode );
7654
7655   const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
7656   //TIDSortedElemSet foundElems;
7657   bool needTheLast = ( theLastNode != 0 );
7658
7659   vector<const SMDS_MeshNode*> nodes;
7660   
7661   while ( nStart != theLastNode ) {
7662     if ( nStart == theFirstNode )
7663       return !needTheLast;
7664
7665     // find all free border faces sharing nStart
7666
7667     list< const SMDS_MeshElement* > curElemList;
7668     list< const SMDS_MeshNode* >    nStartList;
7669     SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
7670     while ( invElemIt->more() ) {
7671       const SMDS_MeshElement* e = invElemIt->next();
7672       //if ( e == curElem || foundElems.insert( e ).second ) // e can encounter twice in border
7673       {
7674         // get nodes
7675         nodes.assign( SMDS_MeshElement::iterator( e->interlacedNodesIterator() ),
7676                       SMDS_MeshElement::iterator() );
7677         nodes.push_back( nodes[ 0 ]);
7678
7679         // check 2 links
7680         int iNode = 0, nbNodes = nodes.size() - 1;
7681         for ( iNode = 0; iNode < nbNodes; iNode++ )
7682           if ((( nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
7683                ( nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
7684               ( ControlFreeBorder( &nodes[ iNode ], e->GetID() )))
7685           {
7686             nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart )]);
7687             curElemList.push_back( e );
7688           }
7689       }
7690     }
7691     // analyse the found
7692
7693     int nbNewBorders = curElemList.size();
7694     if ( nbNewBorders == 0 ) {
7695       // no free border furthermore
7696       return !needTheLast;
7697     }
7698     else if ( nbNewBorders == 1 ) {
7699       // one more element found
7700       nIgnore = nStart;
7701       nStart = nStartList.front();
7702       curElem = curElemList.front();
7703       theFaces.push_back( curElem );
7704       theNodes.push_back( nStart );
7705     }
7706     else {
7707       // several continuations found
7708       list< const SMDS_MeshElement* >::iterator curElemIt;
7709       list< const SMDS_MeshNode* >::iterator nStartIt;
7710       // check if one of them reached the last node
7711       if ( needTheLast ) {
7712         for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7713              curElemIt!= curElemList.end();
7714              curElemIt++, nStartIt++ )
7715           if ( *nStartIt == theLastNode ) {
7716             theFaces.push_back( *curElemIt );
7717             theNodes.push_back( *nStartIt );
7718             return true;
7719           }
7720       }
7721       // find the best free border by the continuations
7722       list<const SMDS_MeshNode*>    contNodes[ 2 ], *cNL;
7723       list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
7724       for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7725            curElemIt!= curElemList.end();
7726            curElemIt++, nStartIt++ )
7727       {
7728         cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
7729         cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
7730         // find one more free border
7731         if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
7732           cNL->clear();
7733           cFL->clear();
7734         }
7735         else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
7736           // choice: clear a worse one
7737           int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
7738           int   iWorse = ( needTheLast ? 1 - iLongest : iLongest );
7739           contNodes[ iWorse ].clear();
7740           contFaces[ iWorse ].clear();
7741         }
7742       }
7743       if ( contNodes[0].empty() && contNodes[1].empty() )
7744         return false;
7745
7746       // push_back the best free border
7747       cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
7748       cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
7749       //theNodes.pop_back(); // remove nIgnore
7750       theNodes.pop_back(); // remove nStart
7751       //theFaces.pop_back(); // remove curElem
7752       theNodes.splice( theNodes.end(), *cNL );
7753       theFaces.splice( theFaces.end(), *cFL );
7754       return true;
7755
7756     } // several continuations found
7757   } // while ( nStart != theLastNode )
7758
7759   return true;
7760 }
7761
7762 //=======================================================================
7763 //function : CheckFreeBorderNodes
7764 //purpose  : Return true if the tree nodes are on a free border
7765 //=======================================================================
7766
7767 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
7768                                             const SMDS_MeshNode* theNode2,
7769                                             const SMDS_MeshNode* theNode3)
7770 {
7771   list< const SMDS_MeshNode* > nodes;
7772   list< const SMDS_MeshElement* > faces;
7773   return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
7774 }
7775
7776 //=======================================================================
7777 //function : SewFreeBorder
7778 //purpose  :
7779 //warning  : for border-to-side sewing theSideSecondNode is considered as
7780 //           the last side node and theSideThirdNode is not used
7781 //=======================================================================
7782
7783 SMESH_MeshEditor::Sew_Error
7784 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
7785                                  const SMDS_MeshNode* theBordSecondNode,
7786                                  const SMDS_MeshNode* theBordLastNode,
7787                                  const SMDS_MeshNode* theSideFirstNode,
7788                                  const SMDS_MeshNode* theSideSecondNode,
7789                                  const SMDS_MeshNode* theSideThirdNode,
7790                                  const bool           theSideIsFreeBorder,
7791                                  const bool           toCreatePolygons,
7792                                  const bool           toCreatePolyedrs)
7793 {
7794   ClearLastCreated();
7795
7796   Sew_Error aResult = SEW_OK;
7797
7798   // ====================================
7799   //    find side nodes and elements
7800   // ====================================
7801
7802   list< const SMDS_MeshNode* >    nSide[ 2 ];
7803   list< const SMDS_MeshElement* > eSide[ 2 ];
7804   list< const SMDS_MeshNode* >::iterator    nIt[ 2 ];
7805   list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
7806
7807   // Free border 1
7808   // --------------
7809   if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
7810                       nSide[0], eSide[0])) {
7811     MESSAGE(" Free Border 1 not found " );
7812     aResult = SEW_BORDER1_NOT_FOUND;
7813   }
7814   if (theSideIsFreeBorder) {
7815     // Free border 2
7816     // --------------
7817     if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
7818                         nSide[1], eSide[1])) {
7819       MESSAGE(" Free Border 2 not found " );
7820       aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
7821     }
7822   }
7823   if ( aResult != SEW_OK )
7824     return aResult;
7825
7826   if (!theSideIsFreeBorder) {
7827     // Side 2
7828     // --------------
7829
7830     // -------------------------------------------------------------------------
7831     // Algo:
7832     // 1. If nodes to merge are not coincident, move nodes of the free border
7833     //    from the coord sys defined by the direction from the first to last
7834     //    nodes of the border to the correspondent sys of the side 2
7835     // 2. On the side 2, find the links most co-directed with the correspondent
7836     //    links of the free border
7837     // -------------------------------------------------------------------------
7838
7839     // 1. Since sewing may break if there are volumes to split on the side 2,
7840     //    we won't move nodes but just compute new coordinates for them
7841     typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
7842     TNodeXYZMap nBordXYZ;
7843     list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
7844     list< const SMDS_MeshNode* >::iterator nBordIt;
7845
7846     gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
7847     gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
7848     gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
7849     gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
7850     double tol2 = 1.e-8;
7851     gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
7852     if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
7853       // Need node movement.
7854
7855       // find X and Z axes to create trsf
7856       gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
7857       gp_Vec X = Zs ^ Zb;
7858       if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
7859         // Zb || Zs
7860         X = gp_Ax2( gp::Origin(), Zb ).XDirection();
7861
7862       // coord systems
7863       gp_Ax3 toBordAx( Pb1, Zb, X );
7864       gp_Ax3 fromSideAx( Ps1, Zs, X );
7865       gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
7866       // set trsf
7867       gp_Trsf toBordSys, fromSide2Sys;
7868       toBordSys.SetTransformation( toBordAx );
7869       fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
7870       fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
7871
7872       // move
7873       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
7874         const SMDS_MeshNode* n = *nBordIt;
7875         gp_XYZ xyz( n->X(),n->Y(),n->Z() );
7876         toBordSys.Transforms( xyz );
7877         fromSide2Sys.Transforms( xyz );
7878         nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
7879       }
7880     }
7881     else {
7882       // just insert nodes XYZ in the nBordXYZ map
7883       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
7884         const SMDS_MeshNode* n = *nBordIt;
7885         nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
7886       }
7887     }
7888
7889     // 2. On the side 2, find the links most co-directed with the correspondent
7890     //    links of the free border
7891
7892     list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
7893     list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
7894     sideNodes.push_back( theSideFirstNode );
7895
7896     bool hasVolumes = false;
7897     LinkID_Gen aLinkID_Gen( GetMeshDS() );
7898     set<long> foundSideLinkIDs, checkedLinkIDs;
7899     SMDS_VolumeTool volume;
7900     //const SMDS_MeshNode* faceNodes[ 4 ];
7901
7902     const SMDS_MeshNode*    sideNode;
7903     const SMDS_MeshElement* sideElem  = 0;
7904     const SMDS_MeshNode* prevSideNode = theSideFirstNode;
7905     const SMDS_MeshNode* prevBordNode = theBordFirstNode;
7906     nBordIt = bordNodes.begin();
7907     nBordIt++;
7908     // border node position and border link direction to compare with
7909     gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
7910     gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
7911     // choose next side node by link direction or by closeness to
7912     // the current border node:
7913     bool searchByDir = ( *nBordIt != theBordLastNode );
7914     do {
7915       // find the next node on the Side 2
7916       sideNode = 0;
7917       double maxDot = -DBL_MAX, minDist = DBL_MAX;
7918       long linkID;
7919       checkedLinkIDs.clear();
7920       gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
7921
7922       // loop on inverse elements of current node (prevSideNode) on the Side 2
7923       SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
7924       while ( invElemIt->more() )
7925       {
7926         const SMDS_MeshElement* elem = invElemIt->next();
7927         // prepare data for a loop on links coming to prevSideNode, of a face or a volume
7928         int iPrevNode = 0, iNode = 0, nbNodes = elem->NbNodes();
7929         vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
7930         bool isVolume = volume.Set( elem );
7931         const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
7932         if ( isVolume ) // --volume
7933           hasVolumes = true;
7934         else if ( elem->GetType() == SMDSAbs_Face ) { // --face
7935           // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
7936           SMDS_NodeIteratorPtr nIt = elem->interlacedNodesIterator();
7937           while ( nIt->more() ) {
7938             nodes[ iNode ] = cast2Node( nIt->next() );
7939             if ( nodes[ iNode++ ] == prevSideNode )
7940               iPrevNode = iNode - 1;
7941           }
7942           // there are 2 links to check
7943           nbNodes = 2;
7944         }
7945         else // --edge
7946           continue;
7947         // loop on links, to be precise, on the second node of links
7948         for ( iNode = 0; iNode < nbNodes; iNode++ ) {
7949           const SMDS_MeshNode* n = nodes[ iNode ];
7950           if ( isVolume ) {
7951             if ( !volume.IsLinked( n, prevSideNode ))
7952               continue;
7953           }
7954           else {
7955             if ( iNode ) // a node before prevSideNode
7956               n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
7957             else         // a node after prevSideNode
7958               n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
7959           }
7960           // check if this link was already used
7961           long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
7962           bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
7963           if (!isJustChecked &&
7964               foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
7965           {
7966             // test a link geometrically
7967             gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
7968             bool linkIsBetter = false;
7969             double dot = 0.0, dist = 0.0;
7970             if ( searchByDir ) { // choose most co-directed link
7971               dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
7972               linkIsBetter = ( dot > maxDot );
7973             }
7974             else { // choose link with the node closest to bordPos
7975               dist = ( nextXYZ - bordPos ).SquareModulus();
7976               linkIsBetter = ( dist < minDist );
7977             }
7978             if ( linkIsBetter ) {
7979               maxDot = dot;
7980               minDist = dist;
7981               linkID = iLink;
7982               sideNode = n;
7983               sideElem = elem;
7984             }
7985           }
7986         }
7987       } // loop on inverse elements of prevSideNode
7988
7989       if ( !sideNode ) {
7990         MESSAGE(" Can't find path by links of the Side 2 ");
7991         return SEW_BAD_SIDE_NODES;
7992       }
7993       sideNodes.push_back( sideNode );
7994       sideElems.push_back( sideElem );
7995       foundSideLinkIDs.insert ( linkID );
7996       prevSideNode = sideNode;
7997
7998       if ( *nBordIt == theBordLastNode )
7999         searchByDir = false;
8000       else {
8001         // find the next border link to compare with
8002         gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8003         searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8004         // move to next border node if sideNode is before forward border node (bordPos)
8005         while ( *nBordIt != theBordLastNode && !searchByDir ) {
8006           prevBordNode = *nBordIt;
8007           nBordIt++;
8008           bordPos = nBordXYZ[ *nBordIt ];
8009           bordDir = bordPos - nBordXYZ[ prevBordNode ];
8010           searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8011         }
8012       }
8013     }
8014     while ( sideNode != theSideSecondNode );
8015
8016     if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8017       MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8018       return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8019     }
8020   } // end nodes search on the side 2
8021
8022   // ============================
8023   // sew the border to the side 2
8024   // ============================
8025
8026   int nbNodes[]  = { (int)nSide[0].size(), (int)nSide[1].size() };
8027   int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8028
8029   bool toMergeConformal = ( nbNodes[0] == nbNodes[1] );
8030   if ( toMergeConformal && toCreatePolygons )
8031   {
8032     // do not merge quadrangles if polygons are OK (IPAL0052824)
8033     eIt[0] = eSide[0].begin();
8034     eIt[1] = eSide[1].begin();
8035     bool allQuads[2] = { true, true };
8036     for ( int iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8037       for ( ; allQuads[iBord] && eIt[iBord] != eSide[iBord].end(); ++eIt[iBord] )
8038         allQuads[iBord] = ( (*eIt[iBord])->NbCornerNodes() == 4 );
8039     }
8040     toMergeConformal = ( !allQuads[0] && !allQuads[1] );
8041   }
8042
8043   TListOfListOfNodes nodeGroupsToMerge;
8044   if (( toMergeConformal ) ||
8045       ( theSideIsFreeBorder && !theSideThirdNode )) {
8046
8047     // all nodes are to be merged
8048
8049     for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8050          nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8051          nIt[0]++, nIt[1]++ )
8052     {
8053       nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8054       nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8055       nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8056     }
8057   }
8058   else {
8059
8060     // insert new nodes into the border and the side to get equal nb of segments
8061
8062     // get normalized parameters of nodes on the borders
8063     vector< double > param[ 2 ];
8064     param[0].resize( maxNbNodes );
8065     param[1].resize( maxNbNodes );
8066     int iNode, iBord;
8067     for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8068       list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8069       list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8070       const SMDS_MeshNode* nPrev = *nIt;
8071       double bordLength = 0;
8072       for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8073         const SMDS_MeshNode* nCur = *nIt;
8074         gp_XYZ segment (nCur->X() - nPrev->X(),
8075                         nCur->Y() - nPrev->Y(),
8076                         nCur->Z() - nPrev->Z());
8077         double segmentLen = segment.Modulus();
8078         bordLength += segmentLen;
8079         param[ iBord ][ iNode ] = bordLength;
8080         nPrev = nCur;
8081       }
8082       // normalize within [0,1]
8083       for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8084         param[ iBord ][ iNode ] /= bordLength;
8085       }
8086     }
8087
8088     // loop on border segments
8089     const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8090     int i[ 2 ] = { 0, 0 };
8091     nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8092     nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8093
8094     // element can be split while iterating on border if it has two edges in the border
8095     std::map< const SMDS_MeshElement* , const SMDS_MeshElement* > elemReplaceMap;
8096     std::map< const SMDS_MeshElement* , const SMDS_MeshElement* >::iterator elemReplaceMapIt;
8097
8098     TElemOfNodeListMap insertMap;
8099     TElemOfNodeListMap::iterator insertMapIt;
8100     // insertMap is
8101     // key:   elem to insert nodes into
8102     // value: 2 nodes to insert between + nodes to be inserted
8103     do {
8104       bool next[ 2 ] = { false, false };
8105
8106       // find min adjacent segment length after sewing
8107       double nextParam = 10., prevParam = 0;
8108       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8109         if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8110           nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8111         if ( i[ iBord ] > 0 )
8112           prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8113       }
8114       double  minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8115       double  maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8116       double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8117
8118       // choose to insert or to merge nodes
8119       double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8120       if ( Abs( du ) <= minSegLen * 0.2 ) {
8121         // merge
8122         // ------
8123         nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8124         const SMDS_MeshNode* n0 = *nIt[0];
8125         const SMDS_MeshNode* n1 = *nIt[1];
8126         nodeGroupsToMerge.back().push_back( n1 );
8127         nodeGroupsToMerge.back().push_back( n0 );
8128         // position of node of the border changes due to merge
8129         param[ 0 ][ i[0] ] += du;
8130         // move n1 for the sake of elem shape evaluation during insertion.
8131         // n1 will be removed by MergeNodes() anyway
8132         const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8133         next[0] = next[1] = true;
8134       }
8135       else {
8136         // insert
8137         // ------
8138         int intoBord = ( du < 0 ) ? 0 : 1;
8139         const SMDS_MeshElement* elem = *eIt [ intoBord ];
8140         const SMDS_MeshNode*    n1   = nPrev[ intoBord ];
8141         const SMDS_MeshNode*    n2   = *nIt [ intoBord ];
8142         const SMDS_MeshNode*    nIns = *nIt [ 1 - intoBord ];
8143         if ( intoBord == 1 ) {
8144           // move node of the border to be on a link of elem of the side
8145           SMESH_NodeXYZ p1( n1 ), p2( n2 );
8146           double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8147           gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8148           GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8149         }
8150         elemReplaceMapIt = elemReplaceMap.find( elem );
8151         if ( elemReplaceMapIt != elemReplaceMap.end() )
8152           elem = elemReplaceMapIt->second;
8153
8154         insertMapIt = insertMap.find( elem );
8155         bool  notFound = ( insertMapIt == insertMap.end() );
8156         bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8157         if ( otherLink ) {
8158           // insert into another link of the same element:
8159           // 1. perform insertion into the other link of the elem
8160           list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8161           const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8162           const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8163           InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8164           // 2. perform insertion into the link of adjacent faces
8165           while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem )) {
8166             InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8167           }
8168           while ( const SMDS_MeshElement* seg = findSegment( n12, n22 )) {
8169             InsertNodesIntoLink( seg, n12, n22, nodeList );
8170           }
8171           if (toCreatePolyedrs) {
8172             // perform insertion into the links of adjacent volumes
8173             UpdateVolumes(n12, n22, nodeList);
8174           }
8175           // 3. find an element appeared on n1 and n2 after the insertion
8176           insertMap.erase( insertMapIt );
8177           const SMDS_MeshElement* elem2 = findAdjacentFace( n1, n2, 0 );
8178           elemReplaceMap.insert( std::make_pair( elem, elem2 ));
8179           elem = elem2;
8180         }
8181         if ( notFound || otherLink ) {
8182           // add element and nodes of the side into the insertMap
8183           insertMapIt = insertMap.insert( make_pair( elem, list<const SMDS_MeshNode*>() )).first;
8184           (*insertMapIt).second.push_back( n1 );
8185           (*insertMapIt).second.push_back( n2 );
8186         }
8187         // add node to be inserted into elem
8188         (*insertMapIt).second.push_back( nIns );
8189         next[ 1 - intoBord ] = true;
8190       }
8191
8192       // go to the next segment
8193       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8194         if ( next[ iBord ] ) {
8195           if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8196             eIt[ iBord ]++;
8197           nPrev[ iBord ] = *nIt[ iBord ];
8198           nIt[ iBord ]++; i[ iBord ]++;
8199         }
8200       }
8201     }
8202     while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8203
8204     // perform insertion of nodes into elements
8205
8206     for (insertMapIt = insertMap.begin();
8207          insertMapIt != insertMap.end();
8208          insertMapIt++ )
8209     {
8210       const SMDS_MeshElement* elem = (*insertMapIt).first;
8211       list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8212       if ( nodeList.size() < 3 ) continue;
8213       const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8214       const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8215
8216       InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8217
8218       while ( const SMDS_MeshElement* seg = findSegment( n1, n2 )) {
8219         InsertNodesIntoLink( seg, n1, n2, nodeList );
8220       }
8221
8222       if ( !theSideIsFreeBorder ) {
8223         // look for and insert nodes into the faces adjacent to elem
8224         while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem )) {
8225           InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8226         }
8227       }
8228       if (toCreatePolyedrs) {
8229         // perform insertion into the links of adjacent volumes
8230         UpdateVolumes(n1, n2, nodeList);
8231       }
8232     }
8233   } // end: insert new nodes
8234
8235   MergeNodes ( nodeGroupsToMerge );
8236
8237
8238   // Remove coincident segments
8239
8240   // get new segments
8241   TIDSortedElemSet segments;
8242   SMESH_SequenceOfElemPtr newFaces;
8243   for ( size_t i = 0; i < myLastCreatedElems.size(); ++i )
8244   {
8245     if ( !myLastCreatedElems[i] ) continue;
8246     if ( myLastCreatedElems[i]->GetType() == SMDSAbs_Edge )
8247       segments.insert( segments.end(), myLastCreatedElems[i] );
8248     else
8249       newFaces.push_back( myLastCreatedElems[i] );
8250   }
8251   // get segments adjacent to merged nodes
8252   TListOfListOfNodes::iterator groupIt = nodeGroupsToMerge.begin();
8253   for ( ; groupIt != nodeGroupsToMerge.end(); groupIt++ )
8254   {
8255     const list<const SMDS_MeshNode*>& nodes = *groupIt;
8256     if ( nodes.front()->IsNull() ) continue;
8257     SMDS_ElemIteratorPtr segIt = nodes.front()->GetInverseElementIterator( SMDSAbs_Edge );
8258     while ( segIt->more() )
8259       segments.insert( segIt->next() );
8260   }
8261
8262   // find coincident
8263   TListOfListOfElementsID equalGroups;
8264   if ( !segments.empty() )
8265     FindEqualElements( segments, equalGroups );
8266   if ( !equalGroups.empty() )
8267   {
8268     // remove from segments those that will be removed
8269     TListOfListOfElementsID::iterator itGroups = equalGroups.begin();
8270     for ( ; itGroups != equalGroups.end(); ++itGroups )
8271     {
8272       list< int >& group = *itGroups;
8273       list< int >::iterator id = group.begin();
8274       for ( ++id; id != group.end(); ++id )
8275         if ( const SMDS_MeshElement* seg = GetMeshDS()->FindElement( *id ))
8276           segments.erase( seg );
8277     }
8278     // remove equal segments
8279     MergeElements( equalGroups );
8280
8281     // restore myLastCreatedElems
8282     myLastCreatedElems = newFaces;
8283     TIDSortedElemSet::iterator seg = segments.begin();
8284     for ( ; seg != segments.end(); ++seg )
8285       myLastCreatedElems.push_back( *seg );
8286   }
8287
8288   return aResult;
8289 }
8290
8291 //=======================================================================
8292 //function : InsertNodesIntoLink
8293 //purpose  : insert theNodesToInsert into theElement between theBetweenNode1
8294 //           and theBetweenNode2 and split theElement
8295 //=======================================================================
8296
8297 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement*     theElement,
8298                                            const SMDS_MeshNode*        theBetweenNode1,
8299                                            const SMDS_MeshNode*        theBetweenNode2,
8300                                            list<const SMDS_MeshNode*>& theNodesToInsert,
8301                                            const bool                  toCreatePoly)
8302 {
8303   if ( !theElement ) return;
8304
8305   SMESHDS_Mesh *aMesh = GetMeshDS();
8306   vector<const SMDS_MeshElement*> newElems;
8307
8308   if ( theElement->GetType() == SMDSAbs_Edge )
8309   {
8310     theNodesToInsert.push_front( theBetweenNode1 );
8311     theNodesToInsert.push_back ( theBetweenNode2 );
8312     list<const SMDS_MeshNode*>::iterator n = theNodesToInsert.begin();
8313     const SMDS_MeshNode* n1 = *n;
8314     for ( ++n; n != theNodesToInsert.end(); ++n )
8315     {
8316       const SMDS_MeshNode* n2 = *n;
8317       if ( const SMDS_MeshElement* seg = aMesh->FindEdge( n1, n2 ))
8318         AddToSameGroups( seg, theElement, aMesh );
8319       else
8320         newElems.push_back( aMesh->AddEdge ( n1, n2 ));
8321       n1 = n2;
8322     }
8323     theNodesToInsert.pop_front();
8324     theNodesToInsert.pop_back();
8325
8326     if ( theElement->IsQuadratic() ) // add a not split part
8327     {
8328       vector<const SMDS_MeshNode*> nodes( theElement->begin_nodes(),
8329                                           theElement->end_nodes() );
8330       int iOther = 0, nbN = nodes.size();
8331       for ( ; iOther < nbN; ++iOther )
8332         if ( nodes[iOther] != theBetweenNode1 &&
8333              nodes[iOther] != theBetweenNode2 )
8334           break;
8335       if      ( iOther == 0 )
8336       {
8337         if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[0], nodes[1] ))
8338           AddToSameGroups( seg, theElement, aMesh );
8339         else
8340           newElems.push_back( aMesh->AddEdge ( nodes[0], nodes[1] ));
8341       }
8342       else if ( iOther == 2 )
8343       {
8344         if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[1], nodes[2] ))
8345           AddToSameGroups( seg, theElement, aMesh );
8346         else
8347           newElems.push_back( aMesh->AddEdge ( nodes[1], nodes[2] ));
8348       }
8349     }
8350     // treat new elements
8351     for ( size_t i = 0; i < newElems.size(); ++i )
8352       if ( newElems[i] )
8353       {
8354         aMesh->SetMeshElementOnShape( newElems[i], theElement->getshapeId() );
8355         myLastCreatedElems.push_back( newElems[i] );
8356       }
8357     ReplaceElemInGroups( theElement, newElems, aMesh );
8358     aMesh->RemoveElement( theElement );
8359     return;
8360
8361   } // if ( theElement->GetType() == SMDSAbs_Edge )
8362
8363   const SMDS_MeshElement* theFace = theElement;
8364   if ( theFace->GetType() != SMDSAbs_Face ) return;
8365
8366   // find indices of 2 link nodes and of the rest nodes
8367   int iNode = 0, il1, il2, i3, i4;
8368   il1 = il2 = i3 = i4 = -1;
8369   vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8370
8371   SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8372   while ( nodeIt->more() ) {
8373     const SMDS_MeshNode* n = nodeIt->next();
8374     if ( n == theBetweenNode1 )
8375       il1 = iNode;
8376     else if ( n == theBetweenNode2 )
8377       il2 = iNode;
8378     else if ( i3 < 0 )
8379       i3 = iNode;
8380     else
8381       i4 = iNode;
8382     nodes[ iNode++ ] = n;
8383   }
8384   if ( il1 < 0 || il2 < 0 || i3 < 0 )
8385     return ;
8386
8387   // arrange link nodes to go one after another regarding the face orientation
8388   bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8389   list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8390   if ( reverse ) {
8391     iNode = il1;
8392     il1 = il2;
8393     il2 = iNode;
8394     aNodesToInsert.reverse();
8395   }
8396   // check that not link nodes of a quadrangles are in good order
8397   int nbFaceNodes = theFace->NbNodes();
8398   if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8399     iNode = i3;
8400     i3 = i4;
8401     i4 = iNode;
8402   }
8403
8404   if (toCreatePoly || theFace->IsPoly()) {
8405
8406     iNode = 0;
8407     vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8408
8409     // add nodes of face up to first node of link
8410     bool isFLN = false;
8411     SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8412     while ( nodeIt->more() && !isFLN ) {
8413       const SMDS_MeshNode* n = nodeIt->next();
8414       poly_nodes[iNode++] = n;
8415       isFLN = ( n == nodes[il1] );
8416     }
8417     // add nodes to insert
8418     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8419     for (; nIt != aNodesToInsert.end(); nIt++) {
8420       poly_nodes[iNode++] = *nIt;
8421     }
8422     // add nodes of face starting from last node of link
8423     while ( nodeIt->more() ) {
8424       const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8425       poly_nodes[iNode++] = n;
8426     }
8427
8428     // make a new face
8429     newElems.push_back( aMesh->AddPolygonalFace( poly_nodes ));
8430   }
8431
8432   else if ( !theFace->IsQuadratic() )
8433   {
8434     // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8435     int nbLinkNodes = 2 + aNodesToInsert.size();
8436     //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8437     vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8438     linkNodes[ 0 ] = nodes[ il1 ];
8439     linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8440     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8441     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8442       linkNodes[ iNode++ ] = *nIt;
8443     }
8444     // decide how to split a quadrangle: compare possible variants
8445     // and choose which of splits to be a quadrangle
8446     int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad = 0;
8447     if ( nbFaceNodes == 3 ) {
8448       iBestQuad = nbSplits;
8449       i4 = i3;
8450     }
8451     else if ( nbFaceNodes == 4 ) {
8452       SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8453       double aBestRate = DBL_MAX;
8454       for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8455         i1 = 0; i2 = 1;
8456         double aBadRate = 0;
8457         // evaluate elements quality
8458         for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8459           if ( iSplit == iQuad ) {
8460             SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8461                                    linkNodes[ i2++ ],
8462                                    nodes[ i3 ],
8463                                    nodes[ i4 ]);
8464             aBadRate += getBadRate( &quad, aCrit );
8465           }
8466           else {
8467             SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8468                                    linkNodes[ i2++ ],
8469                                    nodes[ iSplit < iQuad ? i4 : i3 ]);
8470             aBadRate += getBadRate( &tria, aCrit );
8471           }
8472         }
8473         // choice
8474         if ( aBadRate < aBestRate ) {
8475           iBestQuad = iQuad;
8476           aBestRate = aBadRate;
8477         }
8478       }
8479     }
8480
8481     // create new elements
8482     i1 = 0; i2 = 1;
8483     for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ )
8484     {
8485       if ( iSplit == iBestQuad )
8486         newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8487                                             linkNodes[ i2++ ],
8488                                             nodes[ i3 ],
8489                                             nodes[ i4 ]));
8490       else
8491         newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8492                                             linkNodes[ i2++ ],
8493                                             nodes[ iSplit < iBestQuad ? i4 : i3 ]));
8494     }
8495
8496     const SMDS_MeshNode* newNodes[ 4 ];
8497     newNodes[ 0 ] = linkNodes[ i1 ];
8498     newNodes[ 1 ] = linkNodes[ i2 ];
8499     newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
8500     newNodes[ 3 ] = nodes[ i4 ];
8501     if (iSplit == iBestQuad)
8502       newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] ));
8503     else
8504       newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] ));
8505
8506   } // end if(!theFace->IsQuadratic())
8507
8508   else { // theFace is quadratic
8509     // we have to split theFace on simple triangles and one simple quadrangle
8510     int tmp = il1/2;
8511     int nbshift = tmp*2;
8512     // shift nodes in nodes[] by nbshift
8513     int i,j;
8514     for(i=0; i<nbshift; i++) {
8515       const SMDS_MeshNode* n = nodes[0];
8516       for(j=0; j<nbFaceNodes-1; j++) {
8517         nodes[j] = nodes[j+1];
8518       }
8519       nodes[nbFaceNodes-1] = n;
8520     }
8521     il1 = il1 - nbshift;
8522     // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
8523     //   n0      n1     n2    n0      n1     n2
8524     //     +-----+-----+        +-----+-----+
8525     //      \         /         |           |
8526     //       \       /          |           |
8527     //      n5+     +n3       n7+           +n3
8528     //         \   /            |           |
8529     //          \ /             |           |
8530     //           +              +-----+-----+
8531     //           n4           n6      n5     n4
8532
8533     // create new elements
8534     int n1,n2,n3;
8535     if ( nbFaceNodes == 6 ) { // quadratic triangle
8536       newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8537       if ( theFace->IsMediumNode(nodes[il1]) ) {
8538         // create quadrangle
8539         newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[5] ));
8540         n1 = 1;
8541         n2 = 2;
8542         n3 = 3;
8543       }
8544       else {
8545         // create quadrangle
8546         newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[5] ));
8547         n1 = 0;
8548         n2 = 1;
8549         n3 = 5;
8550       }
8551     }
8552     else { // nbFaceNodes==8 - quadratic quadrangle
8553       newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8554       newElems.push_back( aMesh->AddFace( nodes[5], nodes[6], nodes[7] ));
8555       newElems.push_back( aMesh->AddFace( nodes[5], nodes[7], nodes[3] ));
8556       if ( theFace->IsMediumNode( nodes[ il1 ])) {
8557         // create quadrangle
8558         newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[7] ));
8559         n1 = 1;
8560         n2 = 2;
8561         n3 = 3;
8562       }
8563       else {
8564         // create quadrangle
8565         newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[7] ));
8566         n1 = 0;
8567         n2 = 1;
8568         n3 = 7;
8569       }
8570     }
8571     // create needed triangles using n1,n2,n3 and inserted nodes
8572     int nbn = 2 + aNodesToInsert.size();
8573     vector<const SMDS_MeshNode*> aNodes(nbn);
8574     aNodes[0    ] = nodes[n1];
8575     aNodes[nbn-1] = nodes[n2];
8576     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8577     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8578       aNodes[iNode++] = *nIt;
8579     }
8580     for ( i = 1; i < nbn; i++ )
8581       newElems.push_back( aMesh->AddFace( aNodes[i-1], aNodes[i], nodes[n3] ));
8582   }
8583
8584   // remove the old face
8585   for ( size_t i = 0; i < newElems.size(); ++i )
8586     if ( newElems[i] )
8587     {
8588       aMesh->SetMeshElementOnShape( newElems[i], theFace->getshapeId() );
8589       myLastCreatedElems.push_back( newElems[i] );
8590     }
8591   ReplaceElemInGroups( theFace, newElems, aMesh );
8592   aMesh->RemoveElement(theFace);
8593
8594 } // InsertNodesIntoLink()
8595
8596 //=======================================================================
8597 //function : UpdateVolumes
8598 //purpose  :
8599 //=======================================================================
8600
8601 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode*        theBetweenNode1,
8602                                       const SMDS_MeshNode*        theBetweenNode2,
8603                                       list<const SMDS_MeshNode*>& theNodesToInsert)
8604 {
8605   ClearLastCreated();
8606
8607   SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
8608   while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
8609     const SMDS_MeshElement* elem = invElemIt->next();
8610
8611     // check, if current volume has link theBetweenNode1 - theBetweenNode2
8612     SMDS_VolumeTool aVolume (elem);
8613     if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
8614       continue;
8615
8616     // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
8617     int iface, nbFaces = aVolume.NbFaces();
8618     vector<const SMDS_MeshNode *> poly_nodes;
8619     vector<int> quantities (nbFaces);
8620
8621     for (iface = 0; iface < nbFaces; iface++) {
8622       int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
8623       // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
8624       const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
8625
8626       for (int inode = 0; inode < nbFaceNodes; inode++) {
8627         poly_nodes.push_back(faceNodes[inode]);
8628
8629         if (nbInserted == 0) {
8630           if (faceNodes[inode] == theBetweenNode1) {
8631             if (faceNodes[inode + 1] == theBetweenNode2) {
8632               nbInserted = theNodesToInsert.size();
8633
8634               // add nodes to insert
8635               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
8636               for (; nIt != theNodesToInsert.end(); nIt++) {
8637                 poly_nodes.push_back(*nIt);
8638               }
8639             }
8640           }
8641           else if (faceNodes[inode] == theBetweenNode2) {
8642             if (faceNodes[inode + 1] == theBetweenNode1) {
8643               nbInserted = theNodesToInsert.size();
8644
8645               // add nodes to insert in reversed order
8646               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
8647               nIt--;
8648               for (; nIt != theNodesToInsert.begin(); nIt--) {
8649                 poly_nodes.push_back(*nIt);
8650               }
8651               poly_nodes.push_back(*nIt);
8652             }
8653           }
8654           else {
8655           }
8656         }
8657       }
8658       quantities[iface] = nbFaceNodes + nbInserted;
8659     }
8660
8661     // Replace the volume
8662     SMESHDS_Mesh *aMesh = GetMeshDS();
8663
8664     if ( SMDS_MeshElement* newElem = aMesh->AddPolyhedralVolume( poly_nodes, quantities ))
8665     {
8666       aMesh->SetMeshElementOnShape( newElem, elem->getshapeId() );
8667       myLastCreatedElems.push_back( newElem );
8668       ReplaceElemInGroups( elem, newElem, aMesh );
8669     }
8670     aMesh->RemoveElement( elem );
8671   }
8672 }
8673
8674 namespace
8675 {
8676   //================================================================================
8677   /*!
8678    * \brief Transform any volume into data of SMDSEntity_Polyhedra
8679    */
8680   //================================================================================
8681
8682   void volumeToPolyhedron( const SMDS_MeshElement*         elem,
8683                            vector<const SMDS_MeshNode *> & nodes,
8684                            vector<int> &                   nbNodeInFaces )
8685   {
8686     nodes.clear();
8687     nbNodeInFaces.clear();
8688     SMDS_VolumeTool vTool ( elem );
8689     for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
8690     {
8691       const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
8692       nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
8693       nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
8694     }
8695   }
8696 }
8697
8698 //=======================================================================
8699 /*!
8700  * \brief Convert elements contained in a sub-mesh to quadratic
8701  * \return int - nb of checked elements
8702  */
8703 //=======================================================================
8704
8705 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh *   theSm,
8706                                              SMESH_MesherHelper& theHelper,
8707                                              const bool          theForce3d)
8708 {
8709   //MESSAGE("convertElemToQuadratic");
8710   int nbElem = 0;
8711   if( !theSm ) return nbElem;
8712
8713   vector<int> nbNodeInFaces;
8714   vector<const SMDS_MeshNode *> nodes;
8715   SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
8716   while(ElemItr->more())
8717   {
8718     nbElem++;
8719     const SMDS_MeshElement* elem = ElemItr->next();
8720     if( !elem ) continue;
8721
8722     // analyse a necessity of conversion
8723     const SMDSAbs_ElementType aType = elem->GetType();
8724     if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
8725       continue;
8726     const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
8727     bool hasCentralNodes = false;
8728     if ( elem->IsQuadratic() )
8729     {
8730       bool alreadyOK;
8731       switch ( aGeomType ) {
8732       case SMDSEntity_Quad_Triangle:
8733       case SMDSEntity_Quad_Quadrangle:
8734       case SMDSEntity_Quad_Hexa:
8735       case SMDSEntity_Quad_Penta:
8736         alreadyOK = !theHelper.GetIsBiQuadratic(); break;
8737
8738       case SMDSEntity_BiQuad_Triangle:
8739       case SMDSEntity_BiQuad_Quadrangle:
8740       case SMDSEntity_TriQuad_Hexa:
8741       case SMDSEntity_BiQuad_Penta:
8742         alreadyOK = theHelper.GetIsBiQuadratic();
8743         hasCentralNodes = true;
8744         break;
8745       default:
8746         alreadyOK = true;
8747       }
8748       // take into account already present medium nodes
8749       switch ( aType ) {
8750       case SMDSAbs_Volume:
8751         theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
8752       case SMDSAbs_Face:
8753         theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
8754       case SMDSAbs_Edge:
8755         theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
8756       default:;
8757       }
8758       if ( alreadyOK )
8759         continue;
8760     }
8761     // get elem data needed to re-create it
8762     //
8763     const int id      = elem->GetID();
8764     const int nbNodes = elem->NbCornerNodes();
8765     nodes.assign(elem->begin_nodes(), elem->end_nodes());
8766     if ( aGeomType == SMDSEntity_Polyhedra )
8767       nbNodeInFaces = static_cast<const SMDS_MeshVolume* >( elem )->GetQuantities();
8768     else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
8769       volumeToPolyhedron( elem, nodes, nbNodeInFaces );
8770
8771     // remove a linear element
8772     GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
8773
8774     // remove central nodes of biquadratic elements (biquad->quad conversion)
8775     if ( hasCentralNodes )
8776       for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
8777         if ( nodes[i]->NbInverseElements() == 0 )
8778           GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
8779
8780     const SMDS_MeshElement* NewElem = 0;
8781
8782     switch( aType )
8783     {
8784     case SMDSAbs_Edge :
8785     {
8786       NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
8787       break;
8788     }
8789     case SMDSAbs_Face :
8790     {
8791       switch(nbNodes)
8792       {
8793       case 3:
8794         NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
8795         break;
8796       case 4:
8797         NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8798         break;
8799       default:
8800         NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
8801       }
8802       break;
8803     }
8804     case SMDSAbs_Volume :
8805     {
8806       switch( aGeomType )
8807       {
8808       case SMDSEntity_Tetra:
8809         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8810         break;
8811       case SMDSEntity_Pyramid:
8812         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
8813         break;
8814       case SMDSEntity_Penta:
8815       case SMDSEntity_Quad_Penta:
8816       case SMDSEntity_BiQuad_Penta:
8817         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
8818         break;
8819       case SMDSEntity_Hexa:
8820       case SMDSEntity_Quad_Hexa:
8821       case SMDSEntity_TriQuad_Hexa:
8822         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
8823                                       nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
8824         break;
8825       case SMDSEntity_Hexagonal_Prism:
8826       default:
8827         NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
8828       }
8829       break;
8830     }
8831     default :
8832       continue;
8833     }
8834     ReplaceElemInGroups( elem, NewElem, GetMeshDS());
8835     if( NewElem && NewElem->getshapeId() < 1 )
8836       theSm->AddElement( NewElem );
8837   }
8838   return nbElem;
8839 }
8840 //=======================================================================
8841 //function : ConvertToQuadratic
8842 //purpose  :
8843 //=======================================================================
8844
8845 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
8846 {
8847   //MESSAGE("ConvertToQuadratic "<< theForce3d << " " << theToBiQuad);
8848   SMESHDS_Mesh* meshDS = GetMeshDS();
8849
8850   SMESH_MesherHelper aHelper(*myMesh);
8851
8852   aHelper.SetIsQuadratic( true );
8853   aHelper.SetIsBiQuadratic( theToBiQuad );
8854   aHelper.SetElementsOnShape(true);
8855   aHelper.ToFixNodeParameters( true );
8856
8857   // convert elements assigned to sub-meshes
8858   int nbCheckedElems = 0;
8859   if ( myMesh->HasShapeToMesh() )
8860   {
8861     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
8862     {
8863       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
8864       while ( smIt->more() ) {
8865         SMESH_subMesh* sm = smIt->next();
8866         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
8867           aHelper.SetSubShape( sm->GetSubShape() );
8868           nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
8869         }
8870       }
8871     }
8872   }
8873
8874   // convert elements NOT assigned to sub-meshes
8875   int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
8876   if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
8877   {
8878     aHelper.SetElementsOnShape(false);
8879     SMESHDS_SubMesh *smDS = 0;
8880
8881     // convert edges
8882     SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
8883     while( aEdgeItr->more() )
8884     {
8885       const SMDS_MeshEdge* edge = aEdgeItr->next();
8886       if ( !edge->IsQuadratic() )
8887       {
8888         int                  id = edge->GetID();
8889         const SMDS_MeshNode* n1 = edge->GetNode(0);
8890         const SMDS_MeshNode* n2 = edge->GetNode(1);
8891
8892         meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
8893
8894         const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
8895         ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
8896       }
8897       else
8898       {
8899         aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
8900       }
8901     }
8902
8903     // convert faces
8904     SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
8905     while( aFaceItr->more() )
8906     {
8907       const SMDS_MeshFace* face = aFaceItr->next();
8908       if ( !face ) continue;
8909       
8910       const SMDSAbs_EntityType type = face->GetEntityType();
8911       bool alreadyOK;
8912       switch( type )
8913       {
8914       case SMDSEntity_Quad_Triangle:
8915       case SMDSEntity_Quad_Quadrangle:
8916         alreadyOK = !theToBiQuad;
8917         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
8918         break;
8919       case SMDSEntity_BiQuad_Triangle:
8920       case SMDSEntity_BiQuad_Quadrangle:
8921         alreadyOK = theToBiQuad;
8922         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
8923         break;
8924       default: alreadyOK = false;
8925       }
8926       if ( alreadyOK )
8927         continue;
8928
8929       const int id = face->GetID();
8930       vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
8931
8932       meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
8933
8934       SMDS_MeshFace * NewFace = 0;
8935       switch( type )
8936       {
8937       case SMDSEntity_Triangle:
8938       case SMDSEntity_Quad_Triangle:
8939       case SMDSEntity_BiQuad_Triangle:
8940         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
8941         if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
8942           GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
8943         break;
8944
8945       case SMDSEntity_Quadrangle:
8946       case SMDSEntity_Quad_Quadrangle:
8947       case SMDSEntity_BiQuad_Quadrangle:
8948         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8949         if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
8950           GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
8951         break;
8952
8953       default:;
8954         NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
8955       }
8956       ReplaceElemInGroups( face, NewFace, GetMeshDS());
8957     }
8958
8959     // convert volumes
8960     vector<int> nbNodeInFaces;
8961     SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
8962     while(aVolumeItr->more())
8963     {
8964       const SMDS_MeshVolume* volume = aVolumeItr->next();
8965       if ( !volume ) continue;
8966
8967       const SMDSAbs_EntityType type = volume->GetEntityType();
8968       if ( volume->IsQuadratic() )
8969       {
8970         bool alreadyOK;
8971         switch ( type )
8972         {
8973         case SMDSEntity_Quad_Hexa:    alreadyOK = !theToBiQuad; break;
8974         case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
8975         case SMDSEntity_Quad_Penta:   alreadyOK = !theToBiQuad; break;
8976         case SMDSEntity_BiQuad_Penta: alreadyOK = theToBiQuad; break;
8977         default:                      alreadyOK = true;
8978         }
8979         if ( alreadyOK )
8980         {
8981           aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
8982           continue;
8983         }
8984       }
8985       const int id = volume->GetID();
8986       vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
8987       if ( type == SMDSEntity_Polyhedra )
8988         nbNodeInFaces = static_cast<const SMDS_MeshVolume* >(volume)->GetQuantities();
8989       else if ( type == SMDSEntity_Hexagonal_Prism )
8990         volumeToPolyhedron( volume, nodes, nbNodeInFaces );
8991
8992       meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
8993
8994       SMDS_MeshVolume * NewVolume = 0;
8995       switch ( type )
8996       {
8997       case SMDSEntity_Tetra:
8998         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
8999         break;
9000       case SMDSEntity_Hexa:
9001       case SMDSEntity_Quad_Hexa:
9002       case SMDSEntity_TriQuad_Hexa:
9003         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9004                                       nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9005         for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
9006           if ( nodes[i]->NbInverseElements() == 0 )
9007             GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9008         break;
9009       case SMDSEntity_Pyramid:
9010         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9011                                       nodes[3], nodes[4], id, theForce3d);
9012         break;
9013       case SMDSEntity_Penta:
9014       case SMDSEntity_Quad_Penta:
9015       case SMDSEntity_BiQuad_Penta:
9016         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9017                                       nodes[3], nodes[4], nodes[5], id, theForce3d);
9018         for ( size_t i = 15; i < nodes.size(); ++i ) // rm central nodes
9019           if ( nodes[i]->NbInverseElements() == 0 )
9020             GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9021         break;
9022       case SMDSEntity_Hexagonal_Prism:
9023       default:
9024         NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9025       }
9026       ReplaceElemInGroups(volume, NewVolume, meshDS);
9027     }
9028   }
9029
9030   if ( !theForce3d )
9031   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9032     // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9033     // aHelper.FixQuadraticElements(myError);
9034     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9035   }
9036 }
9037
9038 //================================================================================
9039 /*!
9040  * \brief Makes given elements quadratic
9041  *  \param theForce3d - if true, the medium nodes will be placed in the middle of link
9042  *  \param theElements - elements to make quadratic
9043  */
9044 //================================================================================
9045
9046 void SMESH_MeshEditor::ConvertToQuadratic(const bool        theForce3d,
9047                                           TIDSortedElemSet& theElements,
9048                                           const bool        theToBiQuad)
9049 {
9050   if ( theElements.empty() ) return;
9051
9052   // we believe that all theElements are of the same type
9053   const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9054
9055   // get all nodes shared by theElements
9056   TIDSortedNodeSet allNodes;
9057   TIDSortedElemSet::iterator eIt = theElements.begin();
9058   for ( ; eIt != theElements.end(); ++eIt )
9059     allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9060
9061   // complete theElements with elements of lower dim whose all nodes are in allNodes
9062
9063   TIDSortedElemSet quadAdjacentElems    [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9064   TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9065   TIDSortedNodeSet::iterator nIt = allNodes.begin();
9066   for ( ; nIt != allNodes.end(); ++nIt )
9067   {
9068     const SMDS_MeshNode* n = *nIt;
9069     SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9070     while ( invIt->more() )
9071     {
9072       const SMDS_MeshElement*      e = invIt->next();
9073       const SMDSAbs_ElementType type = e->GetType();
9074       if ( e->IsQuadratic() )
9075       {
9076         quadAdjacentElems[ type ].insert( e );
9077
9078         bool alreadyOK;
9079         switch ( e->GetEntityType() ) {
9080         case SMDSEntity_Quad_Triangle:
9081         case SMDSEntity_Quad_Quadrangle:
9082         case SMDSEntity_Quad_Hexa:         alreadyOK = !theToBiQuad; break;
9083         case SMDSEntity_BiQuad_Triangle:
9084         case SMDSEntity_BiQuad_Quadrangle:
9085         case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; break;
9086         default:                           alreadyOK = true;
9087         }
9088         if ( alreadyOK )
9089           continue;
9090       }
9091       if ( type >= elemType )
9092         continue; // same type or more complex linear element
9093
9094       if ( !checkedAdjacentElems[ type ].insert( e ).second )
9095         continue; // e is already checked
9096
9097       // check nodes
9098       bool allIn = true;
9099       SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9100       while ( nodeIt->more() && allIn )
9101         allIn = allNodes.count( nodeIt->next() );
9102       if ( allIn )
9103         theElements.insert(e );
9104     }
9105   }
9106
9107   SMESH_MesherHelper helper(*myMesh);
9108   helper.SetIsQuadratic( true );
9109   helper.SetIsBiQuadratic( theToBiQuad );
9110
9111   // add links of quadratic adjacent elements to the helper
9112
9113   if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9114     for ( eIt  = quadAdjacentElems[SMDSAbs_Edge].begin();
9115           eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9116     {
9117       helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9118     }
9119   if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9120     for ( eIt  = quadAdjacentElems[SMDSAbs_Face].begin();
9121           eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9122     {
9123       helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9124     }
9125   if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9126     for ( eIt  = quadAdjacentElems[SMDSAbs_Volume].begin();
9127           eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9128     {
9129       helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9130     }
9131
9132   // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9133
9134   SMESHDS_Mesh*  meshDS = GetMeshDS();
9135   SMESHDS_SubMesh* smDS = 0;
9136   for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9137   {
9138     const SMDS_MeshElement* elem = *eIt;
9139
9140     bool alreadyOK;
9141     int nbCentralNodes = 0;
9142     switch ( elem->GetEntityType() ) {
9143       // linear convertible
9144     case SMDSEntity_Edge:
9145     case SMDSEntity_Triangle:
9146     case SMDSEntity_Quadrangle:
9147     case SMDSEntity_Tetra:
9148     case SMDSEntity_Pyramid:
9149     case SMDSEntity_Hexa:
9150     case SMDSEntity_Penta:             alreadyOK = false;       nbCentralNodes = 0; break;
9151       // quadratic that can become bi-quadratic
9152     case SMDSEntity_Quad_Triangle:
9153     case SMDSEntity_Quad_Quadrangle:
9154     case SMDSEntity_Quad_Hexa:         alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9155       // bi-quadratic
9156     case SMDSEntity_BiQuad_Triangle:
9157     case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9158     case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9159       // the rest
9160     default:                           alreadyOK = true;
9161     }
9162     if ( alreadyOK ) continue;
9163
9164     const SMDSAbs_ElementType type = elem->GetType();
9165     const int                   id = elem->GetID();
9166     const int              nbNodes = elem->NbCornerNodes();
9167     vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9168
9169     helper.SetSubShape( elem->getshapeId() );
9170
9171     if ( !smDS || !smDS->Contains( elem ))
9172       smDS = meshDS->MeshElements( elem->getshapeId() );
9173     meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9174
9175     SMDS_MeshElement * newElem = 0;
9176     switch( nbNodes )
9177     {
9178     case 4: // cases for most frequently used element types go first (for optimization)
9179       if ( type == SMDSAbs_Volume )
9180         newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9181       else
9182         newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9183       break;
9184     case 8:
9185       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9186                                  nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9187       break;
9188     case 3:
9189       newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], id, theForce3d);
9190       break;
9191     case 2:
9192       newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9193       break;
9194     case 5:
9195       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9196                                  nodes[4], id, theForce3d);
9197       break;
9198     case 6:
9199       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9200                                  nodes[4], nodes[5], id, theForce3d);
9201       break;
9202     default:;
9203     }
9204     ReplaceElemInGroups( elem, newElem, meshDS);
9205     if( newElem && smDS )
9206       smDS->AddElement( newElem );
9207
9208     // remove central nodes
9209     for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9210       if ( nodes[i]->NbInverseElements() == 0 )
9211         meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9212
9213   } // loop on theElements
9214
9215   if ( !theForce3d )
9216   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9217     // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9218     // helper.FixQuadraticElements( myError );
9219     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9220   }
9221 }
9222
9223 //=======================================================================
9224 /*!
9225  * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9226  * \return int - nb of checked elements
9227  */
9228 //=======================================================================
9229
9230 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh *    theSm,
9231                                      SMDS_ElemIteratorPtr theItr,
9232                                      const int            /*theShapeID*/)
9233 {
9234   int nbElem = 0;
9235   SMESHDS_Mesh* meshDS = GetMeshDS();
9236   ElemFeatures elemType;
9237   vector<const SMDS_MeshNode *> nodes;
9238
9239   while( theItr->more() )
9240   {
9241     const SMDS_MeshElement* elem = theItr->next();
9242     nbElem++;
9243     if( elem && elem->IsQuadratic())
9244     {
9245       // get elem data
9246       int nbCornerNodes = elem->NbCornerNodes();
9247       nodes.assign( elem->begin_nodes(), elem->end_nodes() );
9248
9249       elemType.Init( elem, /*basicOnly=*/false ).SetID( elem->GetID() ).SetQuad( false );
9250
9251       //remove a quadratic element
9252       if ( !theSm || !theSm->Contains( elem ))
9253         theSm = meshDS->MeshElements( elem->getshapeId() );
9254       meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9255
9256       // remove medium nodes
9257       for ( size_t i = nbCornerNodes; i < nodes.size(); ++i )
9258         if ( nodes[i]->NbInverseElements() == 0 )
9259           meshDS->RemoveFreeNode( nodes[i], theSm );
9260
9261       // add a linear element
9262       nodes.resize( nbCornerNodes );
9263       SMDS_MeshElement * newElem = AddElement( nodes, elemType );
9264       ReplaceElemInGroups(elem, newElem, meshDS);
9265       if( theSm && newElem )
9266         theSm->AddElement( newElem );
9267     }
9268   }
9269   return nbElem;
9270 }
9271
9272 //=======================================================================
9273 //function : ConvertFromQuadratic
9274 //purpose  :
9275 //=======================================================================
9276
9277 bool SMESH_MeshEditor::ConvertFromQuadratic()
9278 {
9279   int nbCheckedElems = 0;
9280   if ( myMesh->HasShapeToMesh() )
9281   {
9282     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9283     {
9284       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9285       while ( smIt->more() ) {
9286         SMESH_subMesh* sm = smIt->next();
9287         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9288           nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9289       }
9290     }
9291   }
9292
9293   int totalNbElems =
9294     GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9295   if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9296   {
9297     SMESHDS_SubMesh *aSM = 0;
9298     removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9299   }
9300
9301   return true;
9302 }
9303
9304 namespace
9305 {
9306   //================================================================================
9307   /*!
9308    * \brief Return true if all medium nodes of the element are in the node set
9309    */
9310   //================================================================================
9311
9312   bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9313   {
9314     for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9315       if ( !nodeSet.count( elem->GetNode(i) ))
9316         return false;
9317     return true;
9318   }
9319 }
9320
9321 //================================================================================
9322 /*!
9323  * \brief Makes given elements linear
9324  */
9325 //================================================================================
9326
9327 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9328 {
9329   if ( theElements.empty() ) return;
9330
9331   // collect IDs of medium nodes of theElements; some of these nodes will be removed
9332   set<int> mediumNodeIDs;
9333   TIDSortedElemSet::iterator eIt = theElements.begin();
9334   for ( ; eIt != theElements.end(); ++eIt )
9335   {
9336     const SMDS_MeshElement* e = *eIt;
9337     for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9338       mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9339   }
9340
9341   // replace given elements by linear ones
9342   SMDS_ElemIteratorPtr elemIt = SMESHUtils::elemSetIterator( theElements );
9343   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9344
9345   // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9346   // except those elements sharing medium nodes of quadratic element whose medium nodes
9347   // are not all in mediumNodeIDs
9348
9349   // get remaining medium nodes
9350   TIDSortedNodeSet mediumNodes;
9351   set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9352   for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9353     if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9354       mediumNodes.insert( mediumNodes.end(), n );
9355
9356   // find more quadratic elements to convert
9357   TIDSortedElemSet moreElemsToConvert;
9358   TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9359   for ( ; nIt != mediumNodes.end(); ++nIt )
9360   {
9361     SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9362     while ( invIt->more() )
9363     {
9364       const SMDS_MeshElement* e = invIt->next();
9365       if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9366       {
9367         // find a more complex element including e and
9368         // whose medium nodes are not in mediumNodes
9369         bool complexFound = false;
9370         for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9371         {
9372           SMDS_ElemIteratorPtr invIt2 =
9373             (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9374           while ( invIt2->more() )
9375           {
9376             const SMDS_MeshElement* eComplex = invIt2->next();
9377             if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9378             {
9379               int nbCommonNodes = SMESH_MeshAlgos::NbCommonNodes( e, eComplex );
9380               if ( nbCommonNodes == e->NbNodes())
9381               {
9382                 complexFound = true;
9383                 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9384                 break;
9385               }
9386             }
9387           }
9388         }
9389         if ( !complexFound )
9390           moreElemsToConvert.insert( e );
9391       }
9392     }
9393   }
9394   elemIt = SMESHUtils::elemSetIterator( moreElemsToConvert );
9395   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9396 }
9397
9398 //=======================================================================
9399 //function : SewSideElements
9400 //purpose  :
9401 //=======================================================================
9402
9403 SMESH_MeshEditor::Sew_Error
9404 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet&    theSide1,
9405                                    TIDSortedElemSet&    theSide2,
9406                                    const SMDS_MeshNode* theFirstNode1,
9407                                    const SMDS_MeshNode* theFirstNode2,
9408                                    const SMDS_MeshNode* theSecondNode1,
9409                                    const SMDS_MeshNode* theSecondNode2)
9410 {
9411   ClearLastCreated();
9412
9413   if ( theSide1.size() != theSide2.size() )
9414     return SEW_DIFF_NB_OF_ELEMENTS;
9415
9416   Sew_Error aResult = SEW_OK;
9417   // Algo:
9418   // 1. Build set of faces representing each side
9419   // 2. Find which nodes of the side 1 to merge with ones on the side 2
9420   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9421
9422   // =======================================================================
9423   // 1. Build set of faces representing each side:
9424   // =======================================================================
9425   // a. build set of nodes belonging to faces
9426   // b. complete set of faces: find missing faces whose nodes are in set of nodes
9427   // c. create temporary faces representing side of volumes if correspondent
9428   //    face does not exist
9429
9430   SMESHDS_Mesh* aMesh = GetMeshDS();
9431   // TODO algorithm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9432   //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9433   TIDSortedElemSet             faceSet1, faceSet2;
9434   set<const SMDS_MeshElement*> volSet1,  volSet2;
9435   set<const SMDS_MeshNode*>    nodeSet1, nodeSet2;
9436   TIDSortedElemSet             * faceSetPtr[] = { &faceSet1, &faceSet2 };
9437   set<const SMDS_MeshElement*> *  volSetPtr[] = { &volSet1,  &volSet2  };
9438   set<const SMDS_MeshNode*>    * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9439   TIDSortedElemSet             * elemSetPtr[] = { &theSide1, &theSide2 };
9440   int iSide, iFace, iNode;
9441
9442   list<const SMDS_MeshElement* > tempFaceList;
9443   for ( iSide = 0; iSide < 2; iSide++ ) {
9444     set<const SMDS_MeshNode*>    * nodeSet = nodeSetPtr[ iSide ];
9445     TIDSortedElemSet             * elemSet = elemSetPtr[ iSide ];
9446     TIDSortedElemSet             * faceSet = faceSetPtr[ iSide ];
9447     set<const SMDS_MeshElement*> * volSet  = volSetPtr [ iSide ];
9448     set<const SMDS_MeshElement*>::iterator vIt;
9449     TIDSortedElemSet::iterator eIt;
9450     set<const SMDS_MeshNode*>::iterator    nIt;
9451
9452     // check that given nodes belong to given elements
9453     const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9454     const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9455     int firstIndex = -1, secondIndex = -1;
9456     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9457       const SMDS_MeshElement* elem = *eIt;
9458       if ( firstIndex  < 0 ) firstIndex  = elem->GetNodeIndex( n1 );
9459       if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9460       if ( firstIndex > -1 && secondIndex > -1 ) break;
9461     }
9462     if ( firstIndex < 0 || secondIndex < 0 ) {
9463       // we can simply return until temporary faces created
9464       return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9465     }
9466
9467     // -----------------------------------------------------------
9468     // 1a. Collect nodes of existing faces
9469     //     and build set of face nodes in order to detect missing
9470     //     faces corresponding to sides of volumes
9471     // -----------------------------------------------------------
9472
9473     set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9474
9475     // loop on the given element of a side
9476     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9477       //const SMDS_MeshElement* elem = *eIt;
9478       const SMDS_MeshElement* elem = *eIt;
9479       if ( elem->GetType() == SMDSAbs_Face ) {
9480         faceSet->insert( elem );
9481         set <const SMDS_MeshNode*> faceNodeSet;
9482         SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9483         while ( nodeIt->more() ) {
9484           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9485           nodeSet->insert( n );
9486           faceNodeSet.insert( n );
9487         }
9488         setOfFaceNodeSet.insert( faceNodeSet );
9489       }
9490       else if ( elem->GetType() == SMDSAbs_Volume )
9491         volSet->insert( elem );
9492     }
9493     // ------------------------------------------------------------------------------
9494     // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9495     // ------------------------------------------------------------------------------
9496
9497     for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9498       SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9499       while ( fIt->more() ) { // loop on faces sharing a node
9500         const SMDS_MeshElement* f = fIt->next();
9501         if ( faceSet->find( f ) == faceSet->end() ) {
9502           // check if all nodes are in nodeSet and
9503           // complete setOfFaceNodeSet if they are
9504           set <const SMDS_MeshNode*> faceNodeSet;
9505           SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9506           bool allInSet = true;
9507           while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9508             const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9509             if ( nodeSet->find( n ) == nodeSet->end() )
9510               allInSet = false;
9511             else
9512               faceNodeSet.insert( n );
9513           }
9514           if ( allInSet ) {
9515             faceSet->insert( f );
9516             setOfFaceNodeSet.insert( faceNodeSet );
9517           }
9518         }
9519       }
9520     }
9521
9522     // -------------------------------------------------------------------------
9523     // 1c. Create temporary faces representing sides of volumes if correspondent
9524     //     face does not exist
9525     // -------------------------------------------------------------------------
9526
9527     if ( !volSet->empty() ) {
9528       //int nodeSetSize = nodeSet->size();
9529
9530       // loop on given volumes
9531       for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
9532         SMDS_VolumeTool vol (*vIt);
9533         // loop on volume faces: find free faces
9534         // --------------------------------------
9535         list<const SMDS_MeshElement* > freeFaceList;
9536         for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
9537           if ( !vol.IsFreeFace( iFace ))
9538             continue;
9539           // check if there is already a face with same nodes in a face set
9540           const SMDS_MeshElement* aFreeFace = 0;
9541           const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
9542           int nbNodes = vol.NbFaceNodes( iFace );
9543           set <const SMDS_MeshNode*> faceNodeSet;
9544           vol.GetFaceNodes( iFace, faceNodeSet );
9545           bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
9546           if ( isNewFace ) {
9547             // no such a face is given but it still can exist, check it
9548             vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
9549             aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
9550           }
9551           if ( !aFreeFace ) {
9552             // create a temporary face
9553             if ( nbNodes == 3 ) {
9554               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
9555               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
9556             }
9557             else if ( nbNodes == 4 ) {
9558               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9559               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9560             }
9561             else {
9562               vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9563               //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
9564               aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
9565             }
9566             if ( aFreeFace )
9567               tempFaceList.push_back( aFreeFace );
9568           }
9569
9570           if ( aFreeFace )
9571             freeFaceList.push_back( aFreeFace );
9572
9573         } // loop on faces of a volume
9574
9575         // choose one of several free faces of a volume
9576         // --------------------------------------------
9577         if ( freeFaceList.size() > 1 ) {
9578           // choose a face having max nb of nodes shared by other elems of a side
9579           int maxNbNodes = -1;
9580           list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
9581           while ( fIt != freeFaceList.end() ) { // loop on free faces
9582             int nbSharedNodes = 0;
9583             SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9584             while ( nodeIt->more() ) { // loop on free face nodes
9585               const SMDS_MeshNode* n =
9586                 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9587               SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
9588               while ( invElemIt->more() ) {
9589                 const SMDS_MeshElement* e = invElemIt->next();
9590                 nbSharedNodes += faceSet->count( e );
9591                 nbSharedNodes += elemSet->count( e );
9592               }
9593             }
9594             if ( nbSharedNodes > maxNbNodes ) {
9595               maxNbNodes = nbSharedNodes;
9596               freeFaceList.erase( freeFaceList.begin(), fIt++ );
9597             }
9598             else if ( nbSharedNodes == maxNbNodes ) {
9599               fIt++;
9600             }
9601             else {
9602               freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
9603             }
9604           }
9605           if ( freeFaceList.size() > 1 )
9606           {
9607             // could not choose one face, use another way
9608             // choose a face most close to the bary center of the opposite side
9609             gp_XYZ aBC( 0., 0., 0. );
9610             set <const SMDS_MeshNode*> addedNodes;
9611             TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
9612             eIt = elemSet2->begin();
9613             for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
9614               SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
9615               while ( nodeIt->more() ) { // loop on free face nodes
9616                 const SMDS_MeshNode* n =
9617                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9618                 if ( addedNodes.insert( n ).second )
9619                   aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
9620               }
9621             }
9622             aBC /= addedNodes.size();
9623             double minDist = DBL_MAX;
9624             fIt = freeFaceList.begin();
9625             while ( fIt != freeFaceList.end() ) { // loop on free faces
9626               double dist = 0;
9627               SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9628               while ( nodeIt->more() ) { // loop on free face nodes
9629                 const SMDS_MeshNode* n =
9630                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9631                 gp_XYZ p( n->X(),n->Y(),n->Z() );
9632                 dist += ( aBC - p ).SquareModulus();
9633               }
9634               if ( dist < minDist ) {
9635                 minDist = dist;
9636                 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9637               }
9638               else
9639                 fIt = freeFaceList.erase( fIt++ );
9640             }
9641           }
9642         } // choose one of several free faces of a volume
9643
9644         if ( freeFaceList.size() == 1 ) {
9645           const SMDS_MeshElement* aFreeFace = freeFaceList.front();
9646           faceSet->insert( aFreeFace );
9647           // complete a node set with nodes of a found free face
9648           //           for ( iNode = 0; iNode < ; iNode++ )
9649           //             nodeSet->insert( fNodes[ iNode ] );
9650         }
9651
9652       } // loop on volumes of a side
9653
9654       //       // complete a set of faces if new nodes in a nodeSet appeared
9655       //       // ----------------------------------------------------------
9656       //       if ( nodeSetSize != nodeSet->size() ) {
9657       //         for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9658       //           SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9659       //           while ( fIt->more() ) { // loop on faces sharing a node
9660       //             const SMDS_MeshElement* f = fIt->next();
9661       //             if ( faceSet->find( f ) == faceSet->end() ) {
9662       //               // check if all nodes are in nodeSet and
9663       //               // complete setOfFaceNodeSet if they are
9664       //               set <const SMDS_MeshNode*> faceNodeSet;
9665       //               SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9666       //               bool allInSet = true;
9667       //               while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9668       //                 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9669       //                 if ( nodeSet->find( n ) == nodeSet->end() )
9670       //                   allInSet = false;
9671       //                 else
9672       //                   faceNodeSet.insert( n );
9673       //               }
9674       //               if ( allInSet ) {
9675       //                 faceSet->insert( f );
9676       //                 setOfFaceNodeSet.insert( faceNodeSet );
9677       //               }
9678       //             }
9679       //           }
9680       //         }
9681       //       }
9682     } // Create temporary faces, if there are volumes given
9683   } // loop on sides
9684
9685   if ( faceSet1.size() != faceSet2.size() ) {
9686     // delete temporary faces: they are in reverseElements of actual nodes
9687     //    SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9688     //    while ( tmpFaceIt->more() )
9689     //      aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9690     //    list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9691     //    for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9692     //      aMesh->RemoveElement(*tmpFaceIt);
9693     MESSAGE("Diff nb of faces");
9694     return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9695   }
9696
9697   // ============================================================
9698   // 2. Find nodes to merge:
9699   //              bind a node to remove to a node to put instead
9700   // ============================================================
9701
9702   TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
9703   if ( theFirstNode1 != theFirstNode2 )
9704     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9705   if ( theSecondNode1 != theSecondNode2 )
9706     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9707
9708   LinkID_Gen aLinkID_Gen( GetMeshDS() );
9709   set< long > linkIdSet; // links to process
9710   linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
9711
9712   typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
9713   list< NLink > linkList[2];
9714   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9715   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9716   // loop on links in linkList; find faces by links and append links
9717   // of the found faces to linkList
9718   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9719   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
9720   {
9721     NLink link[] = { *linkIt[0], *linkIt[1] };
9722     long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
9723     if ( !linkIdSet.count( linkID ) )
9724       continue;
9725
9726     // by links, find faces in the face sets,
9727     // and find indices of link nodes in the found faces;
9728     // in a face set, there is only one or no face sharing a link
9729     // ---------------------------------------------------------------
9730
9731     const SMDS_MeshElement* face[] = { 0, 0 };
9732     vector<const SMDS_MeshNode*> fnodes[2];
9733     int iLinkNode[2][2];
9734     TIDSortedElemSet avoidSet;
9735     for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9736       const SMDS_MeshNode* n1 = link[iSide].first;
9737       const SMDS_MeshNode* n2 = link[iSide].second;
9738       //cout << "Side " << iSide << " ";
9739       //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
9740       // find a face by two link nodes
9741       face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
9742                                                       *faceSetPtr[ iSide ], avoidSet,
9743                                                       &iLinkNode[iSide][0],
9744                                                       &iLinkNode[iSide][1] );
9745       if ( face[ iSide ])
9746       {
9747         //cout << " F " << face[ iSide]->GetID() <<endl;
9748         faceSetPtr[ iSide ]->erase( face[ iSide ]);
9749         // put face nodes to fnodes
9750         SMDS_MeshElement::iterator nIt( face[ iSide ]->interlacedNodesIterator() ), nEnd;
9751         fnodes[ iSide ].assign( nIt, nEnd );
9752         fnodes[ iSide ].push_back( fnodes[ iSide ].front());
9753       }
9754     }
9755
9756     // check similarity of elements of the sides
9757     if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
9758       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
9759       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
9760         aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9761       }
9762       else {
9763         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9764       }
9765       break; // do not return because it's necessary to remove tmp faces
9766     }
9767
9768     // set nodes to merge
9769     // -------------------
9770
9771     if ( face[0] && face[1] )  {
9772       const int nbNodes = face[0]->NbNodes();
9773       if ( nbNodes != face[1]->NbNodes() ) {
9774         MESSAGE("Diff nb of face nodes");
9775         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9776         break; // do not return because it s necessary to remove tmp faces
9777       }
9778       bool reverse[] = { false, false }; // order of nodes in the link
9779       for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9780         // analyse link orientation in faces
9781         int i1 = iLinkNode[ iSide ][ 0 ];
9782         int i2 = iLinkNode[ iSide ][ 1 ];
9783         reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
9784       }
9785       int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
9786       int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
9787       for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
9788       {
9789         nReplaceMap.insert  ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
9790                                           fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
9791       }
9792
9793       // add other links of the faces to linkList
9794       // -----------------------------------------
9795
9796       for ( iNode = 0; iNode < nbNodes; iNode++ )  {
9797         linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
9798         pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
9799         if ( !iter_isnew.second ) { // already in a set: no need to process
9800           linkIdSet.erase( iter_isnew.first );
9801         }
9802         else // new in set == encountered for the first time: add
9803         {
9804           const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
9805           const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
9806           linkList[0].push_back ( NLink( n1, n2 ));
9807           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
9808         }
9809       }
9810     } // 2 faces found
9811
9812     if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
9813       break;
9814
9815   } // loop on link lists
9816
9817   if ( aResult == SEW_OK &&
9818        ( //linkIt[0] != linkList[0].end() ||
9819         !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
9820     MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
9821              " " << (faceSetPtr[1]->empty()));
9822     aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9823   }
9824
9825   // ====================================================================
9826   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9827   // ====================================================================
9828
9829   // delete temporary faces
9830   //  SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9831   //  while ( tmpFaceIt->more() )
9832   //    aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9833   list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9834   for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9835     aMesh->RemoveElement(*tmpFaceIt);
9836
9837   if ( aResult != SEW_OK)
9838     return aResult;
9839
9840   list< int > nodeIDsToRemove;
9841   vector< const SMDS_MeshNode*> nodes;
9842   ElemFeatures elemType;
9843
9844   // loop on nodes replacement map
9845   TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
9846   for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
9847     if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second )
9848     {
9849       const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
9850       nodeIDsToRemove.push_back( nToRemove->GetID() );
9851       // loop on elements sharing nToRemove
9852       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
9853       while ( invElemIt->more() ) {
9854         const SMDS_MeshElement* e = invElemIt->next();
9855         // get a new suite of nodes: make replacement
9856         int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
9857         nodes.resize( nbNodes );
9858         SMDS_ElemIteratorPtr nIt = e->nodesIterator();
9859         while ( nIt->more() ) {
9860           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
9861           nnIt = nReplaceMap.find( n );
9862           if ( nnIt != nReplaceMap.end() ) {
9863             nbReplaced++;
9864             n = (*nnIt).second;
9865           }
9866           nodes[ i++ ] = n;
9867         }
9868         //       if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
9869         //         elemIDsToRemove.push_back( e->GetID() );
9870         //       else
9871         if ( nbReplaced )
9872         {
9873           elemType.Init( e, /*basicOnly=*/false ).SetID( e->GetID() );
9874           aMesh->RemoveElement( e );
9875
9876           if ( SMDS_MeshElement* newElem = this->AddElement( nodes, elemType ))
9877           {
9878             AddToSameGroups( newElem, e, aMesh );
9879             if ( int aShapeId = e->getshapeId() )
9880               aMesh->SetMeshElementOnShape( newElem, aShapeId );
9881           }
9882         }
9883       }
9884     }
9885
9886   Remove( nodeIDsToRemove, true );
9887
9888   return aResult;
9889 }
9890
9891 //================================================================================
9892 /*!
9893  * \brief Find corresponding nodes in two sets of faces
9894  * \param theSide1 - first face set
9895  * \param theSide2 - second first face
9896  * \param theFirstNode1 - a boundary node of set 1
9897  * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
9898  * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
9899  * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
9900  * \param nReplaceMap - output map of corresponding nodes
9901  * \return bool  - is a success or not
9902  */
9903 //================================================================================
9904
9905 #ifdef _DEBUG_
9906 //#define DEBUG_MATCHING_NODES
9907 #endif
9908
9909 SMESH_MeshEditor::Sew_Error
9910 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
9911                                     set<const SMDS_MeshElement*>& theSide2,
9912                                     const SMDS_MeshNode*          theFirstNode1,
9913                                     const SMDS_MeshNode*          theFirstNode2,
9914                                     const SMDS_MeshNode*          theSecondNode1,
9915                                     const SMDS_MeshNode*          theSecondNode2,
9916                                     TNodeNodeMap &                nReplaceMap)
9917 {
9918   set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
9919
9920   nReplaceMap.clear();
9921   //if ( theFirstNode1 != theFirstNode2 )
9922   nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9923   //if ( theSecondNode1 != theSecondNode2 )
9924   nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9925
9926   set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
9927   linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
9928
9929   list< NLink > linkList[2];
9930   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9931   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9932
9933   // loop on links in linkList; find faces by links and append links
9934   // of the found faces to linkList
9935   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9936   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
9937     NLink link[] = { *linkIt[0], *linkIt[1] };
9938     if ( linkSet.find( link[0] ) == linkSet.end() )
9939       continue;
9940
9941     // by links, find faces in the face sets,
9942     // and find indices of link nodes in the found faces;
9943     // in a face set, there is only one or no face sharing a link
9944     // ---------------------------------------------------------------
9945
9946     const SMDS_MeshElement* face[] = { 0, 0 };
9947     list<const SMDS_MeshNode*> notLinkNodes[2];
9948     //bool reverse[] = { false, false }; // order of notLinkNodes
9949     int nbNodes[2];
9950     for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
9951     {
9952       const SMDS_MeshNode* n1 = link[iSide].first;
9953       const SMDS_MeshNode* n2 = link[iSide].second;
9954       set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
9955       set< const SMDS_MeshElement* > facesOfNode1;
9956       for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
9957       {
9958         // during a loop of the first node, we find all faces around n1,
9959         // during a loop of the second node, we find one face sharing both n1 and n2
9960         const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
9961         SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
9962         while ( fIt->more() ) { // loop on faces sharing a node
9963           const SMDS_MeshElement* f = fIt->next();
9964           if (faceSet->find( f ) != faceSet->end() && // f is in face set
9965               ! facesOfNode1.insert( f ).second ) // f encounters twice
9966           {
9967             if ( face[ iSide ] ) {
9968               MESSAGE( "2 faces per link " );
9969               return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9970             }
9971             face[ iSide ] = f;
9972             faceSet->erase( f );
9973
9974             // get not link nodes
9975             int nbN = f->NbNodes();
9976             if ( f->IsQuadratic() )
9977               nbN /= 2;
9978             nbNodes[ iSide ] = nbN;
9979             list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
9980             int i1 = f->GetNodeIndex( n1 );
9981             int i2 = f->GetNodeIndex( n2 );
9982             int iEnd = nbN, iBeg = -1, iDelta = 1;
9983             bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
9984             if ( reverse ) {
9985               std::swap( iEnd, iBeg ); iDelta = -1;
9986             }
9987             int i = i2;
9988             while ( true ) {
9989               i += iDelta;
9990               if ( i == iEnd ) i = iBeg + iDelta;
9991               if ( i == i1 ) break;
9992               nodes.push_back ( f->GetNode( i ) );
9993             }
9994           }
9995         }
9996       }
9997     }
9998     // check similarity of elements of the sides
9999     if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10000       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10001       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10002         return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10003       }
10004       else {
10005         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10006       }
10007     }
10008
10009     // set nodes to merge
10010     // -------------------
10011
10012     if ( face[0] && face[1] )  {
10013       if ( nbNodes[0] != nbNodes[1] ) {
10014         MESSAGE("Diff nb of face nodes");
10015         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10016       }
10017 #ifdef DEBUG_MATCHING_NODES
10018       MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10019                 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10020                 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10021 #endif
10022       int nbN = nbNodes[0];
10023       {
10024         list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10025         list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10026         for ( int i = 0 ; i < nbN - 2; ++i ) {
10027 #ifdef DEBUG_MATCHING_NODES
10028           MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10029 #endif
10030           nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10031         }
10032       }
10033
10034       // add other links of the face 1 to linkList
10035       // -----------------------------------------
10036
10037       const SMDS_MeshElement* f0 = face[0];
10038       const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10039       for ( int i = 0; i < nbN; i++ )
10040       {
10041         const SMDS_MeshNode* n2 = f0->GetNode( i );
10042         pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10043           linkSet.insert( SMESH_TLink( n1, n2 ));
10044         if ( !iter_isnew.second ) { // already in a set: no need to process
10045           linkSet.erase( iter_isnew.first );
10046         }
10047         else // new in set == encountered for the first time: add
10048         {
10049 #ifdef DEBUG_MATCHING_NODES
10050           MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10051                     << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10052 #endif
10053           linkList[0].push_back ( NLink( n1, n2 ));
10054           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10055         }
10056         n1 = n2;
10057       }
10058     } // 2 faces found
10059   } // loop on link lists
10060
10061   return SEW_OK;
10062 }
10063
10064 namespace // automatically find theAffectedElems for DoubleNodes()
10065 {
10066   bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem );
10067
10068   //--------------------------------------------------------------------------------
10069   // Nodes shared by adjacent FissureBorder's.
10070   // 1 node  if FissureBorder separates faces
10071   // 2 nodes if FissureBorder separates volumes
10072   struct SubBorder
10073   {
10074     const SMDS_MeshNode* _nodes[2];
10075     int                  _nbNodes;
10076
10077     SubBorder( const SMDS_MeshNode* n1, const SMDS_MeshNode* n2 = 0 )
10078     {
10079       _nodes[0] = n1;
10080       _nodes[1] = n2;
10081       _nbNodes = bool( n1 ) + bool( n2 );
10082       if ( _nbNodes == 2 && n1 > n2 )
10083         std::swap( _nodes[0], _nodes[1] );
10084     }
10085     bool operator<( const SubBorder& other ) const
10086     {
10087       for ( int i = 0; i < _nbNodes; ++i )
10088       {
10089         if ( _nodes[i] < other._nodes[i] ) return true;
10090         if ( _nodes[i] > other._nodes[i] ) return false;
10091       }
10092       return false;
10093     }
10094   };
10095
10096   //--------------------------------------------------------------------------------
10097   // Map a SubBorder to all FissureBorder it bounds
10098   struct FissureBorder;
10099   typedef std::map< SubBorder, std::vector< FissureBorder* > > TBorderLinks;
10100   typedef TBorderLinks::iterator                               TMappedSub;
10101
10102   //--------------------------------------------------------------------------------
10103   /*!
10104    * \brief Element border (volume facet or face edge) at a fissure
10105    */
10106   struct FissureBorder
10107   {
10108     std::vector< const SMDS_MeshNode* > _nodes;    // border nodes
10109     const SMDS_MeshElement*             _elems[2]; // volume or face adjacent to fissure
10110
10111     std::vector< TMappedSub           > _mappedSubs;  // Sub() in TBorderLinks map
10112     std::vector< const SMDS_MeshNode* > _sortedNodes; // to compare FissureBorder's
10113
10114     FissureBorder( FissureBorder && from ) // move constructor
10115     {
10116       std::swap( _nodes,       from._nodes );
10117       std::swap( _sortedNodes, from._sortedNodes );
10118       _elems[0] = from._elems[0];
10119       _elems[1] = from._elems[1];
10120     }
10121
10122     FissureBorder( const SMDS_MeshElement*                  elemToDuplicate,
10123                    std::vector< const SMDS_MeshElement* > & adjElems)
10124       : _nodes( elemToDuplicate->NbCornerNodes() )
10125     {
10126       for ( size_t i = 0; i < _nodes.size(); ++i )
10127         _nodes[i] = elemToDuplicate->GetNode( i );
10128
10129       SMDSAbs_ElementType type = SMDSAbs_ElementType( elemToDuplicate->GetType() + 1 );
10130       findAdjacent( type, adjElems );
10131     }
10132
10133     FissureBorder( const SMDS_MeshNode**                    nodes,
10134                    const size_t                             nbNodes,
10135                    const SMDSAbs_ElementType                adjElemsType,
10136                    std::vector< const SMDS_MeshElement* > & adjElems)
10137       : _nodes( nodes, nodes + nbNodes )
10138     {
10139       findAdjacent( adjElemsType, adjElems );
10140     }
10141
10142     void findAdjacent( const SMDSAbs_ElementType                adjElemsType,
10143                        std::vector< const SMDS_MeshElement* > & adjElems)
10144     {
10145       _elems[0] = _elems[1] = 0;
10146       adjElems.clear();
10147       if ( SMDS_Mesh::GetElementsByNodes( _nodes, adjElems, adjElemsType ))
10148         for ( size_t i = 0; i < adjElems.size() && i < 2; ++i )
10149           _elems[i] = adjElems[i];
10150     }
10151
10152     bool operator<( const FissureBorder& other ) const
10153     {
10154       return GetSortedNodes() < other.GetSortedNodes();
10155     }
10156
10157     const std::vector< const SMDS_MeshNode* >& GetSortedNodes() const
10158     {
10159       if ( _sortedNodes.empty() && !_nodes.empty() )
10160       {
10161         FissureBorder* me = const_cast<FissureBorder*>( this );
10162         me->_sortedNodes = me->_nodes;
10163         std::sort( me->_sortedNodes.begin(), me->_sortedNodes.end() );
10164       }
10165       return _sortedNodes;
10166     }
10167
10168     size_t NbSub() const
10169     {
10170       return _nodes.size();
10171     }
10172
10173     SubBorder Sub(size_t i) const
10174     {
10175       return SubBorder( _nodes[i], NbSub() > 2 ? _nodes[ (i+1)%NbSub() ] : 0 );
10176     }
10177
10178     void AddSelfTo( TBorderLinks& borderLinks )
10179     {
10180       _mappedSubs.resize( NbSub() );
10181       for ( size_t i = 0; i < NbSub(); ++i )
10182       {
10183         TBorderLinks::iterator s2b =
10184           borderLinks.insert( std::make_pair( Sub(i), TBorderLinks::mapped_type() )).first;
10185         s2b->second.push_back( this );
10186         _mappedSubs[ i ] = s2b;
10187       }
10188     }
10189
10190     void Clear()
10191     {
10192       _nodes.clear();
10193     }
10194
10195     const SMDS_MeshElement* GetMarkedElem() const
10196     {
10197       if ( _nodes.empty() ) return 0; // cleared
10198       if ( _elems[0] && _elems[0]->isMarked() ) return _elems[0];
10199       if ( _elems[1] && _elems[1]->isMarked() ) return _elems[1];
10200       return 0;
10201     }
10202
10203     gp_XYZ GetNorm() const // normal to the border
10204     {
10205       gp_XYZ norm;
10206       if ( _nodes.size() == 2 )
10207       {
10208         gp_XYZ avgNorm( 0,0,0 ); // sum of normals of adjacent faces
10209         if ( SMESH_MeshAlgos::FaceNormal( _elems[0], norm ))
10210           avgNorm += norm;
10211         if ( SMESH_MeshAlgos::FaceNormal( _elems[1], norm ))
10212           avgNorm += norm;
10213
10214         gp_XYZ bordDir( SMESH_NodeXYZ( this->_nodes[0] ) - SMESH_NodeXYZ( this->_nodes[1] ));
10215         norm = bordDir ^ avgNorm;
10216       }
10217       else
10218       {
10219         SMESH_NodeXYZ p0( _nodes[0] );
10220         SMESH_NodeXYZ p1( _nodes[1] );
10221         SMESH_NodeXYZ p2( _nodes[2] );
10222         norm = ( p0 - p1 ) ^ ( p2 - p1 );
10223       }
10224       if ( isOut( _nodes[0], norm, GetMarkedElem() ))
10225         norm.Reverse();
10226
10227       return norm;
10228     }
10229
10230     void ChooseSide() // mark an _elem located at positive side of fissure
10231     {
10232       _elems[0]->setIsMarked( true );
10233       gp_XYZ norm = GetNorm();
10234       double maxX = norm.Coord(1);
10235       if ( Abs( maxX ) < Abs( norm.Coord(2)) ) maxX = norm.Coord(2);
10236       if ( Abs( maxX ) < Abs( norm.Coord(3)) ) maxX = norm.Coord(3);
10237       if ( maxX < 0 )
10238       {
10239         _elems[0]->setIsMarked( false );
10240         _elems[1]->setIsMarked( true );
10241       }
10242     }
10243
10244   }; // struct FissureBorder
10245
10246   //--------------------------------------------------------------------------------
10247   /*!
10248    * \brief Classifier of elements at fissure edge
10249    */
10250   class FissureNormal
10251   {
10252     std::vector< gp_XYZ > _normals;
10253     bool                  _bothIn;
10254
10255   public:
10256     void Add( const SMDS_MeshNode* n, const FissureBorder& bord )
10257     {
10258       _bothIn = false;
10259       _normals.reserve(2);
10260       _normals.push_back( bord.GetNorm() );
10261       if ( _normals.size() == 2 )
10262         _bothIn = !isOut( n, _normals[0], bord.GetMarkedElem() );
10263     }
10264
10265     bool IsIn( const SMDS_MeshNode* n, const SMDS_MeshElement* elem ) const
10266     {
10267       bool isIn = false;
10268       switch ( _normals.size() ) {
10269       case 1:
10270       {
10271         isIn = !isOut( n, _normals[0], elem );
10272         break;
10273       }
10274       case 2:
10275       {
10276         bool in1 = !isOut( n, _normals[0], elem );
10277         bool in2 = !isOut( n, _normals[1], elem );
10278         isIn = _bothIn ? ( in1 && in2 ) : ( in1 || in2 );
10279       }
10280       }
10281       return isIn;
10282     }
10283   };
10284
10285   //================================================================================
10286   /*!
10287    * \brief Classify an element by a plane passing through a node
10288    */
10289   //================================================================================
10290
10291   bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem )
10292   {
10293     SMESH_NodeXYZ p = n;
10294     double sumDot = 0;
10295     for ( int i = 0, nb = elem->NbCornerNodes(); i < nb; ++i )
10296     {
10297       SMESH_NodeXYZ pi = elem->GetNode( i );
10298       sumDot += norm * ( pi - p );
10299     }
10300     return sumDot < -1e-100;
10301   }
10302
10303   //================================================================================
10304   /*!
10305    * \brief Find FissureBorder's by nodes to duplicate
10306    */
10307   //================================================================================
10308
10309   void findFissureBorders( const TIDSortedElemSet&        theNodes,
10310                            std::vector< FissureBorder > & theFissureBorders )
10311   {
10312     TIDSortedElemSet::const_iterator nIt = theNodes.begin();
10313     const SMDS_MeshNode* n = dynamic_cast< const SMDS_MeshNode*>( *nIt );
10314     if ( !n ) return;
10315     SMDSAbs_ElementType elemType = SMDSAbs_Volume;
10316     if ( n->NbInverseElements( elemType ) == 0 )
10317     {
10318       elemType = SMDSAbs_Face;
10319       if ( n->NbInverseElements( elemType ) == 0 )
10320         return;
10321     }
10322     // unmark elements touching the fissure
10323     for ( ; nIt != theNodes.end(); ++nIt )
10324       SMESH_MeshAlgos::MarkElems( cast2Node(*nIt)->GetInverseElementIterator(), false );
10325
10326     // loop on elements touching the fissure to get their borders belonging to the fissure
10327     std::set< FissureBorder >              fissureBorders;
10328     std::vector< const SMDS_MeshElement* > adjElems;
10329     std::vector< const SMDS_MeshNode* >    nodes;
10330     SMDS_VolumeTool volTool;
10331     for ( nIt = theNodes.begin(); nIt != theNodes.end(); ++nIt )
10332     {
10333       SMDS_ElemIteratorPtr invIt = cast2Node(*nIt)->GetInverseElementIterator( elemType );
10334       while ( invIt->more() )
10335       {
10336         const SMDS_MeshElement* eInv = invIt->next();
10337         if ( eInv->isMarked() ) continue;
10338         eInv->setIsMarked( true );
10339
10340         if ( elemType == SMDSAbs_Volume )
10341         {
10342           volTool.Set( eInv );
10343           int iQuad = eInv->IsQuadratic() ? 2 : 1;
10344           for ( int iF = 0, nbF = volTool.NbFaces(); iF < nbF; ++iF )
10345           {
10346             const SMDS_MeshNode** nn = volTool.GetFaceNodes( iF );
10347             int                  nbN = volTool.NbFaceNodes( iF ) / iQuad;
10348             nodes.clear();
10349             bool allOnFissure = true;
10350             for ( int iN = 0; iN < nbN  && allOnFissure; iN += iQuad )
10351               if (( allOnFissure = theNodes.count( nn[ iN ])))
10352                 nodes.push_back( nn[ iN ]);
10353             if ( allOnFissure )
10354               fissureBorders.insert( std::move( FissureBorder( &nodes[0], nodes.size(),
10355                                                                elemType, adjElems )));
10356           }
10357         }
10358         else // elemType == SMDSAbs_Face
10359         {
10360           const SMDS_MeshNode* nn[2] = { eInv->GetNode( eInv->NbCornerNodes()-1 ), 0 };
10361           bool            onFissure0 = theNodes.count( nn[0] ), onFissure1;
10362           for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN; ++iN )
10363           {
10364             nn[1]      = eInv->GetNode( iN );
10365             onFissure1 = theNodes.count( nn[1] );
10366             if ( onFissure0 && onFissure1 )
10367               fissureBorders.insert( std::move( FissureBorder( nn, 2, elemType, adjElems )));
10368             nn[0]      = nn[1];
10369             onFissure0 = onFissure1;
10370           }
10371         }
10372       }
10373     }
10374
10375     theFissureBorders.reserve( theFissureBorders.size() + fissureBorders.size());
10376     std::set< FissureBorder >::iterator bord = fissureBorders.begin();
10377     for ( ; bord != fissureBorders.end(); ++bord )
10378     {
10379       theFissureBorders.push_back( std::move( const_cast<FissureBorder&>( *bord ) ));
10380     }
10381     return;
10382   } // findFissureBorders()
10383
10384   //================================================================================
10385   /*!
10386    * \brief Find elements on one side of a fissure defined by elements or nodes to duplicate
10387    *  \param [in] theElemsOrNodes - elements or nodes to duplicate
10388    *  \param [in] theNodesNot - nodes not to duplicate
10389    *  \param [out] theAffectedElems - the found elements
10390    */
10391   //================================================================================
10392
10393   void findAffectedElems( const TIDSortedElemSet& theElemsOrNodes,
10394                           TIDSortedElemSet&       theAffectedElems)
10395   {
10396     if ( theElemsOrNodes.empty() ) return;
10397
10398     // find FissureBorder's
10399
10400     std::vector< FissureBorder >           fissure;
10401     std::vector< const SMDS_MeshElement* > elemsByFacet;
10402
10403     TIDSortedElemSet::const_iterator elIt = theElemsOrNodes.begin();
10404     if ( (*elIt)->GetType() == SMDSAbs_Node )
10405     {
10406       findFissureBorders( theElemsOrNodes, fissure );
10407     }
10408     else
10409     {
10410       fissure.reserve( theElemsOrNodes.size() );
10411       for ( ; elIt != theElemsOrNodes.end(); ++elIt )
10412         fissure.push_back( std::move( FissureBorder( *elIt, elemsByFacet )));
10413     }
10414     if ( fissure.empty() )
10415       return;
10416
10417     // fill borderLinks
10418
10419     TBorderLinks borderLinks;
10420
10421     for ( size_t i = 0; i < fissure.size(); ++i )
10422     {
10423       fissure[i].AddSelfTo( borderLinks );
10424     }
10425
10426     // get theAffectedElems
10427
10428     // unmark elements having nodes on the fissure, theAffectedElems elements will be marked
10429     for ( size_t i = 0; i < fissure.size(); ++i )
10430       for ( size_t j = 0; j < fissure[i]._nodes.size(); ++j )
10431       {
10432         SMESH_MeshAlgos::MarkElemNodes( fissure[i]._nodes[j]->GetInverseElementIterator(),
10433                                         false, /*markElem=*/true );
10434       }
10435
10436     std::vector<const SMDS_MeshNode *>                 facetNodes;
10437     std::map< const SMDS_MeshNode*, FissureNormal >    fissEdgeNodes2Norm;
10438     boost::container::flat_set< const SMDS_MeshNode* > fissureNodes;
10439
10440     // choose a side of fissure
10441     fissure[0].ChooseSide();
10442     theAffectedElems.insert( fissure[0].GetMarkedElem() );
10443
10444     size_t nbCheckedBorders = 0;
10445     while ( nbCheckedBorders < fissure.size() )
10446     {
10447       // find a FissureBorder to treat
10448       FissureBorder* bord = 0;
10449       for ( size_t i = 0; i < fissure.size()  && !bord; ++i )
10450         if ( fissure[i].GetMarkedElem() )
10451           bord = & fissure[i];
10452       for ( size_t i = 0; i < fissure.size()  && !bord; ++i )
10453         if ( fissure[i].NbSub() > 0 && fissure[i]._elems[0] )
10454         {
10455           bord = & fissure[i];
10456           bord->ChooseSide();
10457           theAffectedElems.insert( bord->GetMarkedElem() );
10458         }
10459       if ( !bord ) return;
10460       ++nbCheckedBorders;
10461
10462       // treat FissureBorder's linked to bord
10463       fissureNodes.clear();
10464       fissureNodes.insert( bord->_nodes.begin(), bord->_nodes.end() );
10465       for ( size_t i = 0; i < bord->NbSub(); ++i )
10466       {
10467         TBorderLinks::iterator l2b = bord->_mappedSubs[ i ];
10468         if ( l2b == borderLinks.end() || l2b->second.empty() ) continue;
10469         std::vector< FissureBorder* >& linkedBorders = l2b->second;
10470         const SubBorder&                          sb = l2b->first;
10471         const SMDS_MeshElement*             bordElem = bord->GetMarkedElem();
10472
10473         if ( linkedBorders.size() == 1 ) // fissure edge reached, fill fissEdgeNodes2Norm
10474         {
10475           for ( int j = 0; j < sb._nbNodes; ++j )
10476             fissEdgeNodes2Norm[ sb._nodes[j] ].Add( sb._nodes[j], *bord );
10477           continue;
10478         }
10479
10480         // add to theAffectedElems elems sharing nodes of a SubBorder and a node of bordElem
10481         // until an elem adjacent to a neighbour FissureBorder is found
10482         facetNodes.clear();
10483         facetNodes.insert( facetNodes.end(), sb._nodes, sb._nodes + sb._nbNodes );
10484         facetNodes.resize( sb._nbNodes + 1 );
10485
10486         while ( bordElem )
10487         {
10488           // check if bordElem is adjacent to a neighbour FissureBorder
10489           for ( size_t j = 0; j < linkedBorders.size(); ++j )
10490           {
10491             FissureBorder* bord2 = linkedBorders[j];
10492             if ( bord2 == bord ) continue;
10493             if ( bordElem == bord2->_elems[0] || bordElem == bord2->_elems[1] )
10494               bordElem = 0;
10495             else
10496               fissureNodes.insert( bord2->_nodes.begin(), bord2->_nodes.end() );
10497           }
10498           if ( !bordElem )
10499             break;
10500
10501           // find the next bordElem
10502           const SMDS_MeshElement* nextBordElem = 0;
10503           for ( int iN = 0, nbN = bordElem->NbCornerNodes(); iN < nbN  && !nextBordElem; ++iN )
10504           {
10505             const SMDS_MeshNode* n = bordElem->GetNode( iN );
10506             if ( fissureNodes.count( n )) continue;
10507
10508             facetNodes[ sb._nbNodes ] = n;
10509             elemsByFacet.clear();
10510             if ( SMDS_Mesh::GetElementsByNodes( facetNodes, elemsByFacet ) > 1 )
10511             {
10512               for ( size_t iE = 0; iE < elemsByFacet.size(); ++iE )
10513                 if ( elemsByFacet[ iE ] != bordElem &&
10514                      !elemsByFacet[ iE ]->isMarked() )
10515                 {
10516                   theAffectedElems.insert( elemsByFacet[ iE ]);
10517                   elemsByFacet[ iE ]->setIsMarked( true );
10518                   if ( elemsByFacet[ iE ]->GetType() == bordElem->GetType() )
10519                     nextBordElem = elemsByFacet[ iE ];
10520                 }
10521             }
10522           }
10523           bordElem = nextBordElem;
10524
10525         } // while ( bordElem )
10526
10527         linkedBorders.clear(); // not to treat this link any more
10528
10529       } // loop on SubBorder's of a FissureBorder
10530
10531       bord->Clear();
10532
10533     } // loop on FissureBorder's
10534
10535
10536     // add elements sharing only one node of the fissure, except those sharing fissure edge nodes
10537
10538     // mark nodes of theAffectedElems
10539     SMESH_MeshAlgos::MarkElemNodes( theAffectedElems.begin(), theAffectedElems.end(), true );
10540
10541     // unmark nodes of the fissure
10542     elIt = theElemsOrNodes.begin();
10543     if ( (*elIt)->GetType() == SMDSAbs_Node )
10544       SMESH_MeshAlgos::MarkElems( elIt, theElemsOrNodes.end(), false );
10545     else
10546       SMESH_MeshAlgos::MarkElemNodes( elIt, theElemsOrNodes.end(), false );
10547
10548     std::vector< gp_XYZ > normVec;
10549
10550     // loop on nodes of the fissure, add elements having marked nodes
10551     for ( elIt = theElemsOrNodes.begin(); elIt != theElemsOrNodes.end(); ++elIt )
10552     {
10553       const SMDS_MeshElement* e = (*elIt);
10554       if ( e->GetType() != SMDSAbs_Node )
10555         e->setIsMarked( true ); // avoid adding a fissure element
10556
10557       for ( int iN = 0, nbN = e->NbCornerNodes(); iN < nbN; ++iN )
10558       {
10559         const SMDS_MeshNode* n = e->GetNode( iN );
10560         if ( fissEdgeNodes2Norm.count( n ))
10561           continue;
10562
10563         SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
10564         while ( invIt->more() )
10565         {
10566           const SMDS_MeshElement* eInv = invIt->next();
10567           if ( eInv->isMarked() ) continue;
10568           eInv->setIsMarked( true );
10569
10570           SMDS_ElemIteratorPtr nIt = eInv->nodesIterator();
10571           while( nIt->more() )
10572             if ( nIt->next()->isMarked())
10573             {
10574               theAffectedElems.insert( eInv );
10575               SMESH_MeshAlgos::MarkElems( eInv->nodesIterator(), true );
10576               n->setIsMarked( false );
10577               break;
10578             }
10579         }
10580       }
10581     }
10582
10583     // add elements on the fissure edge
10584     std::map< const SMDS_MeshNode*, FissureNormal >::iterator n2N;
10585     for ( n2N = fissEdgeNodes2Norm.begin(); n2N != fissEdgeNodes2Norm.end(); ++n2N )
10586     {
10587       const SMDS_MeshNode* edgeNode = n2N->first;
10588       const FissureNormal & normals = n2N->second;
10589
10590       SMDS_ElemIteratorPtr invIt = edgeNode->GetInverseElementIterator();
10591       while ( invIt->more() )
10592       {
10593         const SMDS_MeshElement* eInv = invIt->next();
10594         if ( eInv->isMarked() ) continue;
10595         eInv->setIsMarked( true );
10596
10597         // classify eInv using normals
10598         bool toAdd = normals.IsIn( edgeNode, eInv );
10599         if ( toAdd ) // check if all nodes lie on the fissure edge
10600         {
10601           bool notOnEdge = false;
10602           for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN  && !notOnEdge; ++iN )
10603             notOnEdge = !fissEdgeNodes2Norm.count( eInv->GetNode( iN ));
10604           toAdd = notOnEdge;
10605         }
10606         if ( toAdd )
10607         {
10608           theAffectedElems.insert( eInv );
10609         }
10610       }
10611     }
10612
10613     return;
10614   } // findAffectedElems()
10615 } // namespace
10616
10617 //================================================================================
10618 /*!
10619  * \brief Create elements equal (on same nodes) to given ones
10620  *  \param [in] theElements - a set of elems to duplicate. If it is empty, all
10621  *              elements of the uppest dimension are duplicated.
10622  */
10623 //================================================================================
10624
10625 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
10626 {
10627   ClearLastCreated();
10628   SMESHDS_Mesh* mesh = GetMeshDS();
10629
10630   // get an element type and an iterator over elements
10631
10632   SMDSAbs_ElementType type = SMDSAbs_All;
10633   SMDS_ElemIteratorPtr elemIt;
10634   if ( theElements.empty() )
10635   {
10636     if ( mesh->NbNodes() == 0 )
10637       return;
10638     // get most complex type
10639     SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10640       SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10641       SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10642     };
10643     for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10644       if ( mesh->GetMeshInfo().NbElements( types[i] ))
10645       {
10646         type = types[i];
10647         elemIt = mesh->elementsIterator( type );
10648         break;
10649       }
10650   }
10651   else
10652   {
10653     //type = (*theElements.begin())->GetType();
10654     elemIt = SMESHUtils::elemSetIterator( theElements );
10655   }
10656
10657   // un-mark all elements to avoid duplicating just created elements
10658   SMESH_MeshAlgos::MarkElems( mesh->elementsIterator( type ), false );
10659
10660   // duplicate elements
10661
10662   ElemFeatures elemType;
10663
10664   vector< const SMDS_MeshNode* > nodes;
10665   while ( elemIt->more() )
10666   {
10667     const SMDS_MeshElement* elem = elemIt->next();
10668     if (( type != SMDSAbs_All && elem->GetType() != type ) ||
10669         ( elem->isMarked() ))
10670       continue;
10671
10672     elemType.Init( elem, /*basicOnly=*/false );
10673     nodes.assign( elem->begin_nodes(), elem->end_nodes() );
10674
10675     if ( const SMDS_MeshElement* newElem = AddElement( nodes, elemType ))
10676       newElem->setIsMarked( true );
10677   }
10678 }
10679
10680 //================================================================================
10681 /*!
10682   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10683   \param theElems - the list of elements (edges or faces) to be replicated
10684   The nodes for duplication could be found from these elements
10685   \param theNodesNot - list of nodes to NOT replicate
10686   \param theAffectedElems - the list of elements (cells and edges) to which the
10687   replicated nodes should be associated to.
10688   \return TRUE if operation has been completed successfully, FALSE otherwise
10689 */
10690 //================================================================================
10691
10692 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10693                                     const TIDSortedElemSet& theNodesNot,
10694                                     const TIDSortedElemSet& theAffectedElems )
10695 {
10696   ClearLastCreated();
10697
10698   if ( theElems.size() == 0 )
10699     return false;
10700
10701   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10702   if ( !aMeshDS )
10703     return false;
10704
10705   bool res = false;
10706   TNodeNodeMap anOldNodeToNewNode;
10707   // duplicate elements and nodes
10708   res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10709   // replce nodes by duplications
10710   res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10711   return res;
10712 }
10713
10714 //================================================================================
10715 /*!
10716   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10717   \param theMeshDS - mesh instance
10718   \param theElems - the elements replicated or modified (nodes should be changed)
10719   \param theNodesNot - nodes to NOT replicate
10720   \param theNodeNodeMap - relation of old node to new created node
10721   \param theIsDoubleElem - flag os to replicate element or modify
10722   \return TRUE if operation has been completed successfully, FALSE otherwise
10723 */
10724 //================================================================================
10725
10726 bool SMESH_MeshEditor::doubleNodes(SMESHDS_Mesh*           theMeshDS,
10727                                    const TIDSortedElemSet& theElems,
10728                                    const TIDSortedElemSet& theNodesNot,
10729                                    TNodeNodeMap&           theNodeNodeMap,
10730                                    const bool              theIsDoubleElem )
10731 {
10732   // iterate through element and duplicate them (by nodes duplication)
10733   bool res = false;
10734   std::vector<const SMDS_MeshNode*> newNodes;
10735   ElemFeatures elemType;
10736
10737   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10738   for ( ;  elemItr != theElems.end(); ++elemItr )
10739   {
10740     const SMDS_MeshElement* anElem = *elemItr;
10741     // if (!anElem)
10742     //   continue;
10743
10744     // duplicate nodes to duplicate element
10745     bool isDuplicate = false;
10746     newNodes.resize( anElem->NbNodes() );
10747     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10748     int ind = 0;
10749     while ( anIter->more() )
10750     {
10751       const SMDS_MeshNode* aCurrNode = static_cast<const SMDS_MeshNode*>( anIter->next() );
10752       const SMDS_MeshNode*  aNewNode = aCurrNode;
10753       TNodeNodeMap::iterator     n2n = theNodeNodeMap.find( aCurrNode );
10754       if ( n2n != theNodeNodeMap.end() )
10755       {
10756         aNewNode = n2n->second;
10757       }
10758       else if ( theIsDoubleElem && !theNodesNot.count( aCurrNode ))
10759       {
10760         // duplicate node
10761         aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10762         copyPosition( aCurrNode, aNewNode );
10763         theNodeNodeMap[ aCurrNode ] = aNewNode;
10764         myLastCreatedNodes.push_back( aNewNode );
10765       }
10766       isDuplicate |= (aCurrNode != aNewNode);
10767       newNodes[ ind++ ] = aNewNode;
10768     }
10769     if ( !isDuplicate )
10770       continue;
10771
10772     if ( theIsDoubleElem )
10773       AddElement( newNodes, elemType.Init( anElem, /*basicOnly=*/false ));
10774     else
10775       theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], newNodes.size() );
10776
10777     res = true;
10778   }
10779   return res;
10780 }
10781
10782 //================================================================================
10783 /*!
10784   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10785   \param theNodes - identifiers of nodes to be doubled
10786   \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10787   nodes. If list of element identifiers is empty then nodes are doubled but
10788   they not assigned to elements
10789   \return TRUE if operation has been completed successfully, FALSE otherwise
10790 */
10791 //================================================================================
10792
10793 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10794                                     const std::list< int >& theListOfModifiedElems )
10795 {
10796   ClearLastCreated();
10797
10798   if ( theListOfNodes.size() == 0 )
10799     return false;
10800
10801   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10802   if ( !aMeshDS )
10803     return false;
10804
10805   // iterate through nodes and duplicate them
10806
10807   std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10808
10809   std::list< int >::const_iterator aNodeIter;
10810   for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10811   {
10812     const SMDS_MeshNode* aNode = aMeshDS->FindNode( *aNodeIter );
10813     if ( !aNode )
10814       continue;
10815
10816     // duplicate node
10817
10818     const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10819     if ( aNewNode )
10820     {
10821       copyPosition( aNode, aNewNode );
10822       anOldNodeToNewNode[ aNode ] = aNewNode;
10823       myLastCreatedNodes.push_back( aNewNode );
10824     }
10825   }
10826
10827   // Change nodes of elements
10828
10829   std::vector<const SMDS_MeshNode*> aNodeArr;
10830
10831   std::list< int >::const_iterator anElemIter;
10832   for ( anElemIter =  theListOfModifiedElems.begin();
10833         anElemIter != theListOfModifiedElems.end();
10834         anElemIter++ )
10835   {
10836     const SMDS_MeshElement* anElem = aMeshDS->FindElement( *anElemIter );
10837     if ( !anElem )
10838       continue;
10839
10840     aNodeArr.assign( anElem->begin_nodes(), anElem->end_nodes() );
10841     for( size_t i = 0; i < aNodeArr.size(); ++i )
10842     {
10843       std::map< const SMDS_MeshNode*, const SMDS_MeshNode* >::iterator n2n =
10844         anOldNodeToNewNode.find( aNodeArr[ i ]);
10845       if ( n2n != anOldNodeToNewNode.end() )
10846         aNodeArr[ i ] = n2n->second;
10847     }
10848     aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], aNodeArr.size() );
10849   }
10850
10851   return true;
10852 }
10853
10854 namespace {
10855
10856   //================================================================================
10857   /*!
10858     \brief Check if element located inside shape
10859     \return TRUE if IN or ON shape, FALSE otherwise
10860   */
10861   //================================================================================
10862
10863   template<class Classifier>
10864   bool isInside(const SMDS_MeshElement* theElem,
10865                 Classifier&             theClassifier,
10866                 const double            theTol)
10867   {
10868     gp_XYZ centerXYZ (0, 0, 0);
10869     for ( SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator(); aNodeItr->more(); )
10870       centerXYZ += SMESH_NodeXYZ( aNodeItr->next() );
10871
10872     gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10873     theClassifier.Perform(aPnt, theTol);
10874     TopAbs_State aState = theClassifier.State();
10875     return (aState == TopAbs_IN || aState == TopAbs_ON );
10876   }
10877
10878   //================================================================================
10879   /*!
10880    * \brief Classifier of the 3D point on the TopoDS_Face
10881    *        with interaface suitable for isInside()
10882    */
10883   //================================================================================
10884
10885   struct _FaceClassifier
10886   {
10887     Extrema_ExtPS       _extremum;
10888     BRepAdaptor_Surface _surface;
10889     TopAbs_State        _state;
10890
10891     _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10892     {
10893       _extremum.Initialize( _surface,
10894                             _surface.FirstUParameter(), _surface.LastUParameter(),
10895                             _surface.FirstVParameter(), _surface.LastVParameter(),
10896                             _surface.Tolerance(), _surface.Tolerance() );
10897     }
10898     void Perform(const gp_Pnt& aPnt, double theTol)
10899     {
10900       theTol *= theTol;
10901       _state = TopAbs_OUT;
10902       _extremum.Perform(aPnt);
10903       if ( _extremum.IsDone() )
10904         for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10905           _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10906     }
10907     TopAbs_State State() const
10908     {
10909       return _state;
10910     }
10911   };
10912 }
10913
10914 //================================================================================
10915 /*!
10916   \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
10917   This method is the first step of DoubleNodeElemGroupsInRegion.
10918   \param theElems - list of groups of elements (edges or faces) to be replicated
10919   \param theNodesNot - list of groups of nodes not to replicated
10920   \param theShape - shape to detect affected elements (element which geometric center
10921          located on or inside shape). If the shape is null, detection is done on faces orientations
10922          (select elements with a gravity center on the side given by faces normals).
10923          This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
10924          The replicated nodes should be associated to affected elements.
10925   \return true
10926   \sa DoubleNodeElemGroupsInRegion()
10927 */
10928 //================================================================================
10929
10930 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
10931                                                    const TIDSortedElemSet& theNodesNot,
10932                                                    const TopoDS_Shape&     theShape,
10933                                                    TIDSortedElemSet&       theAffectedElems)
10934 {
10935   if ( theShape.IsNull() )
10936   {
10937     findAffectedElems( theElems, theAffectedElems );
10938   }
10939   else
10940   {
10941     const double aTol = Precision::Confusion();
10942     std::unique_ptr< BRepClass3d_SolidClassifier> bsc3d;
10943     std::unique_ptr<_FaceClassifier>              aFaceClassifier;
10944     if ( theShape.ShapeType() == TopAbs_SOLID )
10945     {
10946       bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10947       bsc3d->PerformInfinitePoint(aTol);
10948     }
10949     else if (theShape.ShapeType() == TopAbs_FACE )
10950     {
10951       aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10952     }
10953
10954     // iterates on indicated elements and get elements by back references from their nodes
10955     TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10956     for ( ;  elemItr != theElems.end(); ++elemItr )
10957     {
10958       SMDS_MeshElement*     anElem = (SMDS_MeshElement*)*elemItr;
10959       SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10960       while ( nodeItr->more() )
10961       {
10962         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10963         if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10964           continue;
10965         SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10966         while ( backElemItr->more() )
10967         {
10968           const SMDS_MeshElement* curElem = backElemItr->next();
10969           if ( curElem && theElems.find(curElem) == theElems.end() &&
10970                ( bsc3d.get() ?
10971                  isInside( curElem, *bsc3d, aTol ) :
10972                  isInside( curElem, *aFaceClassifier, aTol )))
10973             theAffectedElems.insert( curElem );
10974         }
10975       }
10976     }
10977   }
10978   return true;
10979 }
10980
10981 //================================================================================
10982 /*!
10983   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10984   \param theElems - group of of elements (edges or faces) to be replicated
10985   \param theNodesNot - group of nodes not to replicate
10986   \param theShape - shape to detect affected elements (element which geometric center
10987   located on or inside shape).
10988   The replicated nodes should be associated to affected elements.
10989   \return TRUE if operation has been completed successfully, FALSE otherwise
10990 */
10991 //================================================================================
10992
10993 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
10994                                             const TIDSortedElemSet& theNodesNot,
10995                                             const TopoDS_Shape&     theShape )
10996 {
10997   if ( theShape.IsNull() )
10998     return false;
10999
11000   const double aTol = Precision::Confusion();
11001   SMESHUtils::Deleter< BRepClass3d_SolidClassifier> bsc3d;
11002   SMESHUtils::Deleter<_FaceClassifier>              aFaceClassifier;
11003   if ( theShape.ShapeType() == TopAbs_SOLID )
11004   {
11005     bsc3d._obj = new BRepClass3d_SolidClassifier( theShape );
11006     bsc3d->PerformInfinitePoint(aTol);
11007   }
11008   else if (theShape.ShapeType() == TopAbs_FACE )
11009   {
11010     aFaceClassifier._obj = new _FaceClassifier( TopoDS::Face( theShape ));
11011   }
11012
11013   // iterates on indicated elements and get elements by back references from their nodes
11014   TIDSortedElemSet anAffected;
11015   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11016   for ( ;  elemItr != theElems.end(); ++elemItr )
11017   {
11018     SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11019     if (!anElem)
11020       continue;
11021
11022     SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11023     while ( nodeItr->more() )
11024     {
11025       const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11026       if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11027         continue;
11028       SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11029       while ( backElemItr->more() )
11030       {
11031         const SMDS_MeshElement* curElem = backElemItr->next();
11032         if ( curElem && theElems.find(curElem) == theElems.end() &&
11033              ( bsc3d ?
11034                isInside( curElem, *bsc3d, aTol ) :
11035                isInside( curElem, *aFaceClassifier, aTol )))
11036           anAffected.insert( curElem );
11037       }
11038     }
11039   }
11040   return DoubleNodes( theElems, theNodesNot, anAffected );
11041 }
11042
11043 /*!
11044  *  \brief compute an oriented angle between two planes defined by four points.
11045  *  The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
11046  *  @param p0 base of the rotation axe
11047  *  @param p1 extremity of the rotation axe
11048  *  @param g1 belongs to the first plane
11049  *  @param g2 belongs to the second plane
11050  */
11051 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
11052 {
11053   gp_Vec vref(p0, p1);
11054   gp_Vec v1(p0, g1);
11055   gp_Vec v2(p0, g2);
11056   gp_Vec n1 = vref.Crossed(v1);
11057   gp_Vec n2 = vref.Crossed(v2);
11058   try {
11059     return n2.AngleWithRef(n1, vref);
11060   }
11061   catch ( Standard_Failure& ) {
11062   }
11063   return Max( v1.Magnitude(), v2.Magnitude() );
11064 }
11065
11066 /*!
11067  * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11068  *  The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
11069  * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
11070  * Triangles are transformed into prisms, and quadrangles into hexahedrons.
11071  * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
11072  * 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.
11073  * 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.
11074  * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
11075  * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
11076  * \param theElems - list of groups of volumes, where a group of volume is a set of
11077  *        SMDS_MeshElements sorted by Id.
11078  * \param createJointElems - if TRUE, create the elements
11079  * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
11080  *        the boundary between \a theDomains and the rest mesh
11081  * \return TRUE if operation has been completed successfully, FALSE otherwise
11082  */
11083 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11084                                                      bool                                 createJointElems,
11085                                                      bool                                 onAllBoundaries)
11086 {
11087   // MESSAGE("----------------------------------------------");
11088   // MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11089   // MESSAGE("----------------------------------------------");
11090
11091   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11092   meshDS->BuildDownWardConnectivity(true);
11093   CHRONO(50);
11094   SMDS_UnstructuredGrid *grid = meshDS->GetGrid();
11095
11096   // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11097   //     build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11098   //     build the list of nodes shared by 2 or more domains, with their domain indexes
11099
11100   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11101   std::map<int,int>celldom; // cell vtkId --> domain
11102   std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains;  // oldNode --> (id domain --> id cell)
11103   std::map<int, std::map<int,int> > nodeDomains; // oldId -->  (domainId --> newId)
11104   faceDomains.clear();
11105   celldom.clear();
11106   cellDomains.clear();
11107   nodeDomains.clear();
11108   std::map<int,int> emptyMap;
11109   std::set<int> emptySet;
11110   emptyMap.clear();
11111
11112   //MESSAGE(".. Number of domains :"<<theElems.size());
11113
11114   TIDSortedElemSet theRestDomElems;
11115   const int iRestDom  = -1;
11116   const int idom0     = onAllBoundaries ? iRestDom : 0;
11117   const int nbDomains = theElems.size();
11118
11119   // Check if the domains do not share an element
11120   for (int idom = 0; idom < nbDomains-1; idom++)
11121   {
11122     //       MESSAGE("... Check of domain #" << idom);
11123     const TIDSortedElemSet& domain = theElems[idom];
11124     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11125     for (; elemItr != domain.end(); ++elemItr)
11126     {
11127       const SMDS_MeshElement* anElem = *elemItr;
11128       int idombisdeb = idom + 1 ;
11129       // check if the element belongs to a domain further in the list
11130       for ( size_t idombis = idombisdeb; idombis < theElems.size(); idombis++ )
11131       {
11132         const TIDSortedElemSet& domainbis = theElems[idombis];
11133         if ( domainbis.count( anElem ))
11134         {
11135           MESSAGE(".... Domain #" << idom);
11136           MESSAGE(".... Domain #" << idombis);
11137           throw SALOME_Exception("The domains are not disjoint.");
11138           return false ;
11139         }
11140       }
11141     }
11142   }
11143
11144   for (int idom = 0; idom < nbDomains; idom++)
11145   {
11146
11147     // --- build a map (face to duplicate --> volume to modify)
11148     //     with all the faces shared by 2 domains (group of elements)
11149     //     and corresponding volume of this domain, for each shared face.
11150     //     a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11151
11152     //MESSAGE("... Neighbors of domain #" << idom);
11153     const TIDSortedElemSet& domain = theElems[idom];
11154     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11155     for (; elemItr != domain.end(); ++elemItr)
11156     {
11157       const SMDS_MeshElement* anElem = *elemItr;
11158       if (!anElem)
11159         continue;
11160       int vtkId = anElem->GetVtkID();
11161       //MESSAGE("  vtkId " << vtkId << " smdsId " << anElem->GetID());
11162       int neighborsVtkIds[NBMAXNEIGHBORS];
11163       int downIds[NBMAXNEIGHBORS];
11164       unsigned char downTypes[NBMAXNEIGHBORS];
11165       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11166       for (int n = 0; n < nbNeighbors; n++)
11167       {
11168         int smdsId = meshDS->FromVtkToSmds(neighborsVtkIds[n]);
11169         const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11170         if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
11171         {
11172           bool ok = false;
11173           for ( size_t idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
11174           {
11175             // MESSAGE("Domain " << idombis);
11176             const TIDSortedElemSet& domainbis = theElems[idombis];
11177             if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11178           }
11179           if ( ok || onAllBoundaries ) // the characteristics of the face is stored
11180           {
11181             DownIdType face(downIds[n], downTypes[n]);
11182             if (!faceDomains[face].count(idom))
11183             {
11184               faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11185               celldom[vtkId] = idom;
11186               //MESSAGE("       cell with a border " << vtkId << " domain " << idom);
11187             }
11188             if ( !ok )
11189             {
11190               theRestDomElems.insert( elem );
11191               faceDomains[face][iRestDom] = neighborsVtkIds[n];
11192               celldom[neighborsVtkIds[n]] = iRestDom;
11193             }
11194           }
11195         }
11196       }
11197     }
11198   }
11199
11200   //MESSAGE("Number of shared faces " << faceDomains.size());
11201   std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11202
11203   // --- explore the shared faces domain by domain,
11204   //     explore the nodes of the face and see if they belong to a cell in the domain,
11205   //     which has only a node or an edge on the border (not a shared face)
11206
11207   for (int idomain = idom0; idomain < nbDomains; idomain++)
11208   {
11209     //MESSAGE("Domain " << idomain);
11210     const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
11211     itface = faceDomains.begin();
11212     for (; itface != faceDomains.end(); ++itface)
11213     {
11214       const std::map<int, int>& domvol = itface->second;
11215       if (!domvol.count(idomain))
11216         continue;
11217       DownIdType face = itface->first;
11218       //MESSAGE(" --- face " << face.cellId);
11219       std::set<int> oldNodes;
11220       oldNodes.clear();
11221       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11222       std::set<int>::iterator itn = oldNodes.begin();
11223       for (; itn != oldNodes.end(); ++itn)
11224       {
11225         int oldId = *itn;
11226         //MESSAGE("     node " << oldId);
11227         vtkCellLinks::Link l = (static_cast <vtkCellLinks *>(grid->GetCellLinks()))->GetLink(oldId);
11228         for (int i=0; i<l.ncells; i++)
11229         {
11230           int vtkId = l.cells[i];
11231           const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->FromVtkToSmds(vtkId));
11232           if (!domain.count(anElem))
11233             continue;
11234           int vtkType = grid->GetCellType(vtkId);
11235           int downId = grid->CellIdToDownId(vtkId);
11236           if (downId < 0)
11237           {
11238             MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11239             continue; // not OK at this stage of the algorithm:
11240             //no cells created after BuildDownWardConnectivity
11241           }
11242           DownIdType aCell(downId, vtkType);
11243           cellDomains[aCell][idomain] = vtkId;
11244           celldom[vtkId] = idomain;
11245           //MESSAGE("       cell " << vtkId << " domain " << idomain);
11246         }
11247       }
11248     }
11249   }
11250
11251   // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11252   //     for each shared face, get the nodes
11253   //     for each node, for each domain of the face, create a clone of the node
11254
11255   // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11256   //     junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11257   //     the value is the ordered domain ids. (more than 4 domains not taken into account)
11258
11259   std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11260   std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11261   std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11262
11263   //MESSAGE(".. Duplication of the nodes");
11264   for (int idomain = idom0; idomain < nbDomains; idomain++)
11265   {
11266     itface = faceDomains.begin();
11267     for (; itface != faceDomains.end(); ++itface)
11268     {
11269       const std::map<int, int>& domvol = itface->second;
11270       if (!domvol.count(idomain))
11271         continue;
11272       DownIdType face = itface->first;
11273       //MESSAGE(" --- face " << face.cellId);
11274       std::set<int> oldNodes;
11275       oldNodes.clear();
11276       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11277       std::set<int>::iterator itn = oldNodes.begin();
11278       for (; itn != oldNodes.end(); ++itn)
11279       {
11280         int oldId = *itn;
11281         if (nodeDomains[oldId].empty())
11282         {
11283           nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11284           //MESSAGE("-+-+-b     oldNode " << oldId << " domain " << idomain);
11285         }
11286         std::map<int, int>::const_iterator itdom = domvol.begin();
11287         for (; itdom != domvol.end(); ++itdom)
11288         {
11289           int idom = itdom->first;
11290           //MESSAGE("         domain " << idom);
11291           if (!nodeDomains[oldId].count(idom)) // --- node to clone
11292           {
11293             if (nodeDomains[oldId].size() >= 2) // a multiple node
11294             {
11295               vector<int> orderedDoms;
11296               //MESSAGE("multiple node " << oldId);
11297               if (mutipleNodes.count(oldId))
11298                 orderedDoms = mutipleNodes[oldId];
11299               else
11300               {
11301                 map<int,int>::iterator it = nodeDomains[oldId].begin();
11302                 for (; it != nodeDomains[oldId].end(); ++it)
11303                   orderedDoms.push_back(it->first);
11304               }
11305               orderedDoms.push_back(idom); // TODO order ==> push_front or back
11306               //stringstream txt;
11307               //for (int i=0; i<orderedDoms.size(); i++)
11308               //  txt << orderedDoms[i] << " ";
11309               //MESSAGE("orderedDoms " << txt.str());
11310               mutipleNodes[oldId] = orderedDoms;
11311             }
11312             double *coords = grid->GetPoint(oldId);
11313             SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11314             copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11315             int newId = newNode->GetVtkID();
11316             nodeDomains[oldId][idom] = newId; // cloned node for other domains
11317             //MESSAGE("-+-+-c     oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11318           }
11319         }
11320       }
11321     }
11322   }
11323
11324   //MESSAGE(".. Creation of elements");
11325   for (int idomain = idom0; idomain < nbDomains; idomain++)
11326   {
11327     itface = faceDomains.begin();
11328     for (; itface != faceDomains.end(); ++itface)
11329     {
11330       std::map<int, int> domvol = itface->second;
11331       if (!domvol.count(idomain))
11332         continue;
11333       DownIdType face = itface->first;
11334       //MESSAGE(" --- face " << face.cellId);
11335       std::set<int> oldNodes;
11336       oldNodes.clear();
11337       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11338       int nbMultipleNodes = 0;
11339       std::set<int>::iterator itn = oldNodes.begin();
11340       for (; itn != oldNodes.end(); ++itn)
11341       {
11342         int oldId = *itn;
11343         if (mutipleNodes.count(oldId))
11344           nbMultipleNodes++;
11345       }
11346       if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11347       {
11348         //MESSAGE("multiple Nodes detected on a shared face");
11349         int downId = itface->first.cellId;
11350         unsigned char cellType = itface->first.cellType;
11351         // --- shared edge or shared face ?
11352         if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11353         {
11354           int nodes[3];
11355           int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11356           for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11357             if (mutipleNodes.count(nodes[i]))
11358               if (!mutipleNodesToFace.count(nodes[i]))
11359                 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11360         }
11361         else // shared face (between two volumes)
11362         {
11363           int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11364           const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11365           const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11366           for (int ie =0; ie < nbEdges; ie++)
11367           {
11368             int nodes[3];
11369             int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11370             if ( mutipleNodes.count(nodes[0]) && mutipleNodes.count( nodes[ nbNodes-1 ]))
11371             {
11372               vector<int> vn0 = mutipleNodes[nodes[0]];
11373               vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11374               vector<int> doms;
11375               for ( size_t i0 = 0; i0 < vn0.size(); i0++ )
11376                 for ( size_t i1 = 0; i1 < vn1.size(); i1++ )
11377                   if ( vn0[i0] == vn1[i1] )
11378                     doms.push_back( vn0[ i0 ]);
11379               if ( doms.size() > 2 )
11380               {
11381                 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11382                 double *coords = grid->GetPoint(nodes[0]);
11383                 gp_Pnt p0(coords[0], coords[1], coords[2]);
11384                 coords = grid->GetPoint(nodes[nbNodes - 1]);
11385                 gp_Pnt p1(coords[0], coords[1], coords[2]);
11386                 gp_Pnt gref;
11387                 int vtkVolIds[1000];  // an edge can belong to a lot of volumes
11388                 map<int, SMDS_MeshVolume*> domvol; // domain --> a volume with the edge
11389                 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11390                 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11391                 for ( size_t id = 0; id < doms.size(); id++ )
11392                 {
11393                   int idom = doms[id];
11394                   const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11395                   for ( int ivol = 0; ivol < nbvol; ivol++ )
11396                   {
11397                     int smdsId = meshDS->FromVtkToSmds(vtkVolIds[ivol]);
11398                     const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11399                     if (domain.count(elem))
11400                     {
11401                       const SMDS_MeshVolume* svol = SMDS_Mesh::DownCast<SMDS_MeshVolume>(elem);
11402                       domvol[idom] = (SMDS_MeshVolume*) svol;
11403                       //MESSAGE("  domain " << idom << " volume " << elem->GetID());
11404                       double values[3] = { 0,0,0 };
11405                       vtkIdType npts = 0;
11406                       vtkIdType const *pts(nullptr);
11407                       grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11408                       for ( vtkIdType i = 0; i < npts; ++i )
11409                       {
11410                         double *coords = grid->GetPoint( pts[i] );
11411                         for ( int j = 0; j < 3; ++j )
11412                           values[j] += coords[j] / npts;
11413                       }
11414                       if ( id == 0 )
11415                       {
11416                         gref.SetCoord( values[0], values[1], values[2] );
11417                         angleDom[idom] = 0;
11418                       }
11419                       else
11420                       {
11421                         gp_Pnt g( values[0], values[1], values[2] );
11422                         angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11423                         //MESSAGE("  angle=" << angleDom[idom]);
11424                       }
11425                       break;
11426                     }
11427                   }
11428                 }
11429                 map<double, int> sortedDom; // sort domains by angle
11430                 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11431                   sortedDom[ia->second] = ia->first;
11432                 vector<int> vnodes;
11433                 vector<int> vdom;
11434                 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11435                 {
11436                   vdom.push_back(ib->second);
11437                   //MESSAGE("  ordered domain " << ib->second << "  angle " << ib->first);
11438                 }
11439                 for (int ino = 0; ino < nbNodes; ino++)
11440                   vnodes.push_back(nodes[ino]);
11441                 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11442               }
11443             }
11444           }
11445         }
11446       }
11447     }
11448   }
11449
11450   // --- iterate on shared faces (volumes to modify, face to extrude)
11451   //     get node id's of the face (id SMDS = id VTK)
11452   //     create flat element with old and new nodes if requested
11453
11454   // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11455   //     (domain1 X domain2) = domain1 + MAXINT*domain2
11456
11457   std::map<int, std::map<long,int> > nodeQuadDomains;
11458   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11459
11460   //MESSAGE(".. Creation of elements: simple junction");
11461   if (createJointElems)
11462   {
11463     string joints2DName = "joints2D";
11464     mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str());
11465     SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11466     string joints3DName = "joints3D";
11467     mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str());
11468     SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11469
11470     itface = faceDomains.begin();
11471     for (; itface != faceDomains.end(); ++itface)
11472     {
11473       DownIdType face = itface->first;
11474       std::set<int> oldNodes;
11475       std::set<int>::iterator itn;
11476       oldNodes.clear();
11477       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11478
11479       std::map<int, int> domvol = itface->second;
11480       std::map<int, int>::iterator itdom = domvol.begin();
11481       int dom1 = itdom->first;
11482       int vtkVolId = itdom->second;
11483       itdom++;
11484       int dom2 = itdom->first;
11485       SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11486                                                        nodeQuadDomains);
11487       stringstream grpname;
11488       grpname << "j_";
11489       if (dom1 < dom2)
11490         grpname << dom1 << "_" << dom2;
11491       else
11492         grpname << dom2 << "_" << dom1;
11493       string namegrp = grpname.str();
11494       if (!mapOfJunctionGroups.count(namegrp))
11495         mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str());
11496       SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11497       if (sgrp)
11498         sgrp->Add(vol->GetID());
11499       if (vol->GetType() == SMDSAbs_Volume)
11500         joints3DGrp->Add(vol->GetID());
11501       else if (vol->GetType() == SMDSAbs_Face)
11502         joints2DGrp->Add(vol->GetID());
11503     }
11504   }
11505
11506   // --- create volumes on multiple domain intersection if requested
11507   //     iterate on mutipleNodesToFace
11508   //     iterate on edgesMultiDomains
11509
11510   //MESSAGE(".. Creation of elements: multiple junction");
11511   if (createJointElems)
11512   {
11513     // --- iterate on mutipleNodesToFace
11514
11515     std::map<int, std::vector<int> >::iterator itn =  mutipleNodesToFace.begin();
11516     for (; itn != mutipleNodesToFace.end(); ++itn)
11517     {
11518       int node = itn->first;
11519       vector<int> orderDom = itn->second;
11520       vector<vtkIdType> orderedNodes;
11521       for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11522         orderedNodes.push_back( nodeDomains[ node ][ orderDom[ idom ]]);
11523       SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11524
11525       stringstream grpname;
11526       grpname << "m2j_";
11527       grpname << 0 << "_" << 0;
11528       string namegrp = grpname.str();
11529       if (!mapOfJunctionGroups.count(namegrp))
11530         mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str());
11531       SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11532       if (sgrp)
11533         sgrp->Add(face->GetID());
11534     }
11535
11536     // --- iterate on edgesMultiDomains
11537
11538     std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11539     for (; ite != edgesMultiDomains.end(); ++ite)
11540     {
11541       vector<int> nodes = ite->first;
11542       vector<int> orderDom = ite->second;
11543       vector<vtkIdType> orderedNodes;
11544       if (nodes.size() == 2)
11545       {
11546         //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11547         for ( size_t ino = 0; ino < nodes.size(); ino++ )
11548           if ( orderDom.size() == 3 )
11549             for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11550               orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11551           else
11552             for (int idom = orderDom.size()-1; idom >=0; idom--)
11553               orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11554         SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11555
11556         string namegrp = "jointsMultiples";
11557         if (!mapOfJunctionGroups.count(namegrp))
11558           mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str());
11559         SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11560         if (sgrp)
11561           sgrp->Add(vol->GetID());
11562       }
11563       else
11564       {
11565         //INFOS("Quadratic multiple joints not implemented");
11566         // TODO quadratic nodes
11567       }
11568     }
11569   }
11570
11571   // --- list the explicit faces and edges of the mesh that need to be modified,
11572   //     i.e. faces and edges built with one or more duplicated nodes.
11573   //     associate these faces or edges to their corresponding domain.
11574   //     only the first domain found is kept when a face or edge is shared
11575
11576   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11577   std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11578   faceOrEdgeDom.clear();
11579   feDom.clear();
11580
11581   //MESSAGE(".. Modification of elements");
11582   for (int idomain = idom0; idomain < nbDomains; idomain++)
11583   {
11584     std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11585     for (; itnod != nodeDomains.end(); ++itnod)
11586     {
11587       int oldId = itnod->first;
11588       //MESSAGE("     node " << oldId);
11589       vtkCellLinks::Link l = (static_cast< vtkCellLinks *>(grid->GetCellLinks()))->GetLink(oldId);
11590       for (int i = 0; i < l.ncells; i++)
11591       {
11592         int vtkId = l.cells[i];
11593         int vtkType = grid->GetCellType(vtkId);
11594         int downId = grid->CellIdToDownId(vtkId);
11595         if (downId < 0)
11596           continue; // new cells: not to be modified
11597         DownIdType aCell(downId, vtkType);
11598         int volParents[1000];
11599         int nbvol = grid->GetParentVolumes(volParents, vtkId);
11600         for (int j = 0; j < nbvol; j++)
11601           if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11602             if (!feDom.count(vtkId))
11603             {
11604               feDom[vtkId] = idomain;
11605               faceOrEdgeDom[aCell] = emptyMap;
11606               faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11607               //MESSAGE("affect cell " << this->GetMeshDS()->FromVtkToSmds(vtkId) << " domain " << idomain
11608               //        << " type " << vtkType << " downId " << downId);
11609             }
11610       }
11611     }
11612   }
11613
11614   // --- iterate on shared faces (volumes to modify, face to extrude)
11615   //     get node id's of the face
11616   //     replace old nodes by new nodes in volumes, and update inverse connectivity
11617
11618   std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11619   for (int m=0; m<3; m++)
11620   {
11621     std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11622     itface = (*amap).begin();
11623     for (; itface != (*amap).end(); ++itface)
11624     {
11625       DownIdType face = itface->first;
11626       std::set<int> oldNodes;
11627       std::set<int>::iterator itn;
11628       oldNodes.clear();
11629       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11630       //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11631       std::map<int, int> localClonedNodeIds;
11632
11633       std::map<int, int> domvol = itface->second;
11634       std::map<int, int>::iterator itdom = domvol.begin();
11635       for (; itdom != domvol.end(); ++itdom)
11636       {
11637         int idom = itdom->first;
11638         int vtkVolId = itdom->second;
11639         //MESSAGE("modify nodes of cell " << this->GetMeshDS()->FromVtkToSmds(vtkVolId) << " domain " << idom);
11640         localClonedNodeIds.clear();
11641         for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11642         {
11643           int oldId = *itn;
11644           if (nodeDomains[oldId].count(idom))
11645           {
11646             localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11647             //MESSAGE("     node " << oldId << " --> " << localClonedNodeIds[oldId]);
11648           }
11649         }
11650         meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11651       }
11652     }
11653   }
11654
11655   // Remove empty groups (issue 0022812)
11656   std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
11657   for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
11658   {
11659     if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
11660       myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
11661   }
11662
11663   meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11664   grid->DeleteLinks();
11665
11666   CHRONOSTOP(50);
11667   counters::stats();
11668   return true;
11669 }
11670
11671 /*!
11672  * \brief Double nodes on some external faces and create flat elements.
11673  * Flat elements are mainly used by some types of mechanic calculations.
11674  *
11675  * Each group of the list must be constituted of faces.
11676  * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11677  * @param theElems - list of groups of faces, where a group of faces is a set of
11678  * SMDS_MeshElements sorted by Id.
11679  * @return TRUE if operation has been completed successfully, FALSE otherwise
11680  */
11681 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11682 {
11683   // MESSAGE("-------------------------------------------------");
11684   // MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11685   // MESSAGE("-------------------------------------------------");
11686
11687   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11688
11689   // --- For each group of faces
11690   //     duplicate the nodes, create a flat element based on the face
11691   //     replace the nodes of the faces by their clones
11692
11693   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11694   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11695   clonedNodes.clear();
11696   intermediateNodes.clear();
11697   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11698   mapOfJunctionGroups.clear();
11699
11700   for ( size_t idom = 0; idom < theElems.size(); idom++ )
11701   {
11702     const TIDSortedElemSet&           domain = theElems[idom];
11703     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11704     for ( ; elemItr != domain.end(); ++elemItr )
11705     {
11706       const SMDS_MeshFace* aFace = meshDS->DownCast<SMDS_MeshFace> ( *elemItr );
11707       if (!aFace)
11708         continue;
11709       // MESSAGE("aFace=" << aFace->GetID());
11710       bool isQuad = aFace->IsQuadratic();
11711       vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11712
11713       // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11714
11715       SMDS_NodeIteratorPtr nodeIt = aFace->nodeIterator();
11716       while (nodeIt->more())
11717       {
11718         const SMDS_MeshNode* node = nodeIt->next();
11719         bool isMedium = ( isQuad && aFace->IsMediumNode( node ));
11720         if (isMedium)
11721           ln2.push_back(node);
11722         else
11723           ln0.push_back(node);
11724
11725         const SMDS_MeshNode* clone = 0;
11726         if (!clonedNodes.count(node))
11727         {
11728           clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11729           copyPosition( node, clone );
11730           clonedNodes[node] = clone;
11731         }
11732         else
11733           clone = clonedNodes[node];
11734
11735         if (isMedium)
11736           ln3.push_back(clone);
11737         else
11738           ln1.push_back(clone);
11739
11740         const SMDS_MeshNode* inter = 0;
11741         if (isQuad && (!isMedium))
11742         {
11743           if (!intermediateNodes.count(node))
11744           {
11745             inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11746             copyPosition( node, inter );
11747             intermediateNodes[node] = inter;
11748           }
11749           else
11750             inter = intermediateNodes[node];
11751           ln4.push_back(inter);
11752         }
11753       }
11754
11755       // --- extrude the face
11756
11757       vector<const SMDS_MeshNode*> ln;
11758       SMDS_MeshVolume* vol = 0;
11759       vtkIdType aType = aFace->GetVtkType();
11760       switch (aType)
11761       {
11762       case VTK_TRIANGLE:
11763         vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11764         // MESSAGE("vol prism " << vol->GetID());
11765         ln.push_back(ln1[0]);
11766         ln.push_back(ln1[1]);
11767         ln.push_back(ln1[2]);
11768         break;
11769       case VTK_QUAD:
11770         vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11771         // MESSAGE("vol hexa " << vol->GetID());
11772         ln.push_back(ln1[0]);
11773         ln.push_back(ln1[1]);
11774         ln.push_back(ln1[2]);
11775         ln.push_back(ln1[3]);
11776         break;
11777       case VTK_QUADRATIC_TRIANGLE:
11778         vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11779                                 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11780         // MESSAGE("vol quad prism " << vol->GetID());
11781         ln.push_back(ln1[0]);
11782         ln.push_back(ln1[1]);
11783         ln.push_back(ln1[2]);
11784         ln.push_back(ln3[0]);
11785         ln.push_back(ln3[1]);
11786         ln.push_back(ln3[2]);
11787         break;
11788       case VTK_QUADRATIC_QUAD:
11789         //              vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11790         //                                      ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11791         //                                      ln4[0], ln4[1], ln4[2], ln4[3]);
11792         vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11793                                 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11794                                 ln4[0], ln4[1], ln4[2], ln4[3]);
11795         // MESSAGE("vol quad hexa " << vol->GetID());
11796         ln.push_back(ln1[0]);
11797         ln.push_back(ln1[1]);
11798         ln.push_back(ln1[2]);
11799         ln.push_back(ln1[3]);
11800         ln.push_back(ln3[0]);
11801         ln.push_back(ln3[1]);
11802         ln.push_back(ln3[2]);
11803         ln.push_back(ln3[3]);
11804         break;
11805       case VTK_POLYGON:
11806         break;
11807       default:
11808         break;
11809       }
11810
11811       if (vol)
11812       {
11813         stringstream grpname;
11814         grpname << "jf_";
11815         grpname << idom;
11816         string namegrp = grpname.str();
11817         if (!mapOfJunctionGroups.count(namegrp))
11818           mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str());
11819         SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11820         if (sgrp)
11821           sgrp->Add(vol->GetID());
11822       }
11823
11824       // --- modify the face
11825
11826       const_cast<SMDS_MeshFace*>( aFace )->ChangeNodes( &ln[0], ln.size() );
11827     }
11828   }
11829   return true;
11830 }
11831
11832 /*!
11833  *  \brief identify all the elements around a geom shape, get the faces delimiting the hole
11834  *  Build groups of volume to remove, groups of faces to replace on the skin of the object,
11835  *  groups of faces to remove inside the object, (idem edges).
11836  *  Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11837  */
11838 void SMESH_MeshEditor::CreateHoleSkin(double                          radius,
11839                                       const TopoDS_Shape&             theShape,
11840                                       SMESH_NodeSearcher*             theNodeSearcher,
11841                                       const char*                     groupName,
11842                                       std::vector<double>&            nodesCoords,
11843                                       std::vector<std::vector<int> >& listOfListOfNodes)
11844 {
11845   // MESSAGE("--------------------------------");
11846   // MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11847   // MESSAGE("--------------------------------");
11848
11849   // --- zone of volumes to remove is given :
11850   //     1 either by a geom shape (one or more vertices) and a radius,
11851   //     2 either by a group of nodes (representative of the shape)to use with the radius,
11852   //     3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11853   //     In the case 2, the group of nodes is an external group of nodes from another mesh,
11854   //     In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11855   //     defined by it's name.
11856
11857   SMESHDS_GroupBase* groupDS = 0;
11858   SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11859   while ( groupIt->more() )
11860   {
11861     groupDS = 0;
11862     SMESH_Group * group = groupIt->next();
11863     if ( !group ) continue;
11864     groupDS = group->GetGroupDS();
11865     if ( !groupDS || groupDS->IsEmpty() ) continue;
11866     std::string grpName = group->GetName();
11867     //MESSAGE("grpName=" << grpName);
11868     if (grpName == groupName)
11869       break;
11870     else
11871       groupDS = 0;
11872   }
11873
11874   bool isNodeGroup = false;
11875   bool isNodeCoords = false;
11876   if (groupDS)
11877   {
11878     if (groupDS->GetType() != SMDSAbs_Node)
11879       return;
11880     isNodeGroup = true;     // a group of nodes exists and it is in this mesh
11881   }
11882
11883   if (nodesCoords.size() > 0)
11884     isNodeCoords = true; // a list o nodes given by their coordinates
11885   //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11886
11887   // --- define groups to build
11888
11889   // --- group of SMDS volumes
11890   string grpvName = groupName;
11891   grpvName += "_vol";
11892   SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str());
11893   if (!grp)
11894   {
11895     MESSAGE("group not created " << grpvName);
11896     return;
11897   }
11898   SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
11899
11900   // --- group of SMDS faces on the skin
11901   string grpsName = groupName;
11902   grpsName += "_skin";
11903   SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str());
11904   if (!grps)
11905   {
11906     MESSAGE("group not created " << grpsName);
11907     return;
11908   }
11909   SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
11910
11911   // --- group of SMDS faces internal (several shapes)
11912   string grpiName = groupName;
11913   grpiName += "_internalFaces";
11914   SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str());
11915   if (!grpi)
11916   {
11917     MESSAGE("group not created " << grpiName);
11918     return;
11919   }
11920   SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
11921
11922   // --- group of SMDS faces internal (several shapes)
11923   string grpeiName = groupName;
11924   grpeiName += "_internalEdges";
11925   SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str());
11926   if (!grpei)
11927   {
11928     MESSAGE("group not created " << grpeiName);
11929     return;
11930   }
11931   SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
11932
11933   // --- build downward connectivity
11934
11935   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11936   meshDS->BuildDownWardConnectivity(true);
11937   SMDS_UnstructuredGrid* grid = meshDS->GetGrid();
11938
11939   // --- set of volumes detected inside
11940
11941   std::set<int> setOfInsideVol;
11942   std::set<int> setOfVolToCheck;
11943
11944   std::vector<gp_Pnt> gpnts;
11945   gpnts.clear();
11946
11947   if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
11948   {
11949     //MESSAGE("group of nodes provided");
11950     SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
11951     while ( elemIt->more() )
11952     {
11953       const SMDS_MeshElement* elem = elemIt->next();
11954       if (!elem)
11955         continue;
11956       const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
11957       if (!node)
11958         continue;
11959       SMDS_MeshElement* vol = 0;
11960       SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
11961       while (volItr->more())
11962       {
11963         vol = (SMDS_MeshElement*)volItr->next();
11964         setOfInsideVol.insert(vol->GetVtkID());
11965         sgrp->Add(vol->GetID());
11966       }
11967     }
11968   }
11969   else if (isNodeCoords)
11970   {
11971     //MESSAGE("list of nodes coordinates provided");
11972     size_t i = 0;
11973     int k = 0;
11974     while ( i < nodesCoords.size()-2 )
11975     {
11976       double x = nodesCoords[i++];
11977       double y = nodesCoords[i++];
11978       double z = nodesCoords[i++];
11979       gp_Pnt p = gp_Pnt(x, y ,z);
11980       gpnts.push_back(p);
11981       //MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
11982       k++;
11983     }
11984   }
11985   else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
11986   {
11987     //MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
11988     TopTools_IndexedMapOfShape vertexMap;
11989     TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
11990     gp_Pnt p = gp_Pnt(0,0,0);
11991     if (vertexMap.Extent() < 1)
11992       return;
11993
11994     for ( int i = 1; i <= vertexMap.Extent(); ++i )
11995     {
11996       const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
11997       p = BRep_Tool::Pnt(vertex);
11998       gpnts.push_back(p);
11999       //MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
12000     }
12001   }
12002
12003   if (gpnts.size() > 0)
12004   {
12005     const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
12006     //MESSAGE("startNode->nodeId " << nodeId);
12007
12008     double radius2 = radius*radius;
12009     //MESSAGE("radius2 " << radius2);
12010
12011     // --- volumes on start node
12012
12013     setOfVolToCheck.clear();
12014     SMDS_MeshElement* startVol = 0;
12015     SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
12016     while (volItr->more())
12017     {
12018       startVol = (SMDS_MeshElement*)volItr->next();
12019       setOfVolToCheck.insert(startVol->GetVtkID());
12020     }
12021     if (setOfVolToCheck.empty())
12022     {
12023       MESSAGE("No volumes found");
12024       return;
12025     }
12026
12027     // --- starting with central volumes then their neighbors, check if they are inside
12028     //     or outside the domain, until no more new neighbor volume is inside.
12029     //     Fill the group of inside volumes
12030
12031     std::map<int, double> mapOfNodeDistance2;
12032     mapOfNodeDistance2.clear();
12033     std::set<int> setOfOutsideVol;
12034     while (!setOfVolToCheck.empty())
12035     {
12036       std::set<int>::iterator it = setOfVolToCheck.begin();
12037       int vtkId = *it;
12038       //MESSAGE("volume to check,  vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12039       bool volInside = false;
12040       vtkIdType npts = 0;
12041       vtkIdType const *pts(nullptr);
12042       grid->GetCellPoints(vtkId, npts, pts);
12043       for (int i=0; i<npts; i++)
12044       {
12045         double distance2 = 0;
12046         if (mapOfNodeDistance2.count(pts[i]))
12047         {
12048           distance2 = mapOfNodeDistance2[pts[i]];
12049           //MESSAGE("point " << pts[i] << " distance2 " << distance2);
12050         }
12051         else
12052         {
12053           double *coords = grid->GetPoint(pts[i]);
12054           gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
12055           distance2 = 1.E40;
12056           for ( size_t j = 0; j < gpnts.size(); j++ )
12057           {
12058             double d2 = aPoint.SquareDistance( gpnts[ j ]);
12059             if (d2 < distance2)
12060             {
12061               distance2 = d2;
12062               if (distance2 < radius2)
12063                 break;
12064             }
12065           }
12066           mapOfNodeDistance2[pts[i]] = distance2;
12067           //MESSAGE("  point "  << pts[i]  << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " <<  coords[2]);
12068         }
12069         if (distance2 < radius2)
12070         {
12071           volInside = true; // one or more nodes inside the domain
12072           sgrp->Add(meshDS->FromVtkToSmds(vtkId));
12073           break;
12074         }
12075       }
12076       if (volInside)
12077       {
12078         setOfInsideVol.insert(vtkId);
12079         //MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12080         int neighborsVtkIds[NBMAXNEIGHBORS];
12081         int downIds[NBMAXNEIGHBORS];
12082         unsigned char downTypes[NBMAXNEIGHBORS];
12083         int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12084         for (int n = 0; n < nbNeighbors; n++)
12085           if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12086             setOfVolToCheck.insert(neighborsVtkIds[n]);
12087       }
12088       else
12089       {
12090         setOfOutsideVol.insert(vtkId);
12091         //MESSAGE("  volume outside, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12092       }
12093       setOfVolToCheck.erase(vtkId);
12094     }
12095   }
12096
12097   // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12098   //     If yes, add the volume to the inside set
12099
12100   bool addedInside = true;
12101   std::set<int> setOfVolToReCheck;
12102   while (addedInside)
12103   {
12104     //MESSAGE(" --------------------------- re check");
12105     addedInside = false;
12106     std::set<int>::iterator itv = setOfInsideVol.begin();
12107     for (; itv != setOfInsideVol.end(); ++itv)
12108     {
12109       int vtkId = *itv;
12110       int neighborsVtkIds[NBMAXNEIGHBORS];
12111       int downIds[NBMAXNEIGHBORS];
12112       unsigned char downTypes[NBMAXNEIGHBORS];
12113       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12114       for (int n = 0; n < nbNeighbors; n++)
12115         if (!setOfInsideVol.count(neighborsVtkIds[n]))
12116           setOfVolToReCheck.insert(neighborsVtkIds[n]);
12117     }
12118     setOfVolToCheck = setOfVolToReCheck;
12119     setOfVolToReCheck.clear();
12120     while  (!setOfVolToCheck.empty())
12121     {
12122       std::set<int>::iterator it = setOfVolToCheck.begin();
12123       int vtkId = *it;
12124       if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12125       {
12126         //MESSAGE("volume to recheck,  vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12127         int countInside = 0;
12128         int neighborsVtkIds[NBMAXNEIGHBORS];
12129         int downIds[NBMAXNEIGHBORS];
12130         unsigned char downTypes[NBMAXNEIGHBORS];
12131         int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12132         for (int n = 0; n < nbNeighbors; n++)
12133           if (setOfInsideVol.count(neighborsVtkIds[n]))
12134             countInside++;
12135         //MESSAGE("countInside " << countInside);
12136         if (countInside > 1)
12137         {
12138           //MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12139           setOfInsideVol.insert(vtkId);
12140           sgrp->Add(meshDS->FromVtkToSmds(vtkId));
12141           addedInside = true;
12142         }
12143         else
12144           setOfVolToReCheck.insert(vtkId);
12145       }
12146       setOfVolToCheck.erase(vtkId);
12147     }
12148   }
12149
12150   // --- map of Downward faces at the boundary, inside the global volume
12151   //     map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12152   //     fill group of SMDS faces inside the volume (when several volume shapes)
12153   //     fill group of SMDS faces on the skin of the global volume (if skin)
12154
12155   std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12156   std::map<DownIdType, int, DownIdCompare> skinFaces;     // faces on the skin of the global volume --> corresponding cell
12157   std::set<int>::iterator it = setOfInsideVol.begin();
12158   for (; it != setOfInsideVol.end(); ++it)
12159   {
12160     int vtkId = *it;
12161     //MESSAGE("  vtkId " << vtkId  << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12162     int neighborsVtkIds[NBMAXNEIGHBORS];
12163     int downIds[NBMAXNEIGHBORS];
12164     unsigned char downTypes[NBMAXNEIGHBORS];
12165     int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12166     for (int n = 0; n < nbNeighbors; n++)
12167     {
12168       int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12169       if (neighborDim == 3)
12170       {
12171         if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12172         {
12173           DownIdType face(downIds[n], downTypes[n]);
12174           boundaryFaces[face] = vtkId;
12175         }
12176         // if the face between to volumes is in the mesh, get it (internal face between shapes)
12177         int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12178         if (vtkFaceId >= 0)
12179         {
12180           sgrpi->Add(meshDS->FromVtkToSmds(vtkFaceId));
12181           // find also the smds edges on this face
12182           int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12183           const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12184           const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12185           for (int i = 0; i < nbEdges; i++)
12186           {
12187             int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12188             if (vtkEdgeId >= 0)
12189               sgrpei->Add(meshDS->FromVtkToSmds(vtkEdgeId));
12190           }
12191         }
12192       }
12193       else if (neighborDim == 2) // skin of the volume
12194       {
12195         DownIdType face(downIds[n], downTypes[n]);
12196         skinFaces[face] = vtkId;
12197         int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12198         if (vtkFaceId >= 0)
12199           sgrps->Add(meshDS->FromVtkToSmds(vtkFaceId));
12200       }
12201     }
12202   }
12203
12204   // --- identify the edges constituting the wire of each subshape on the skin
12205   //     define polylines with the nodes of edges, equivalent to wires
12206   //     project polylines on subshapes, and partition, to get geom faces
12207
12208   std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12209   std::set<int> emptySet;
12210   emptySet.clear();
12211   std::set<int> shapeIds;
12212
12213   SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12214   while (itelem->more())
12215   {
12216     const SMDS_MeshElement *elem = itelem->next();
12217     int shapeId = elem->getshapeId();
12218     int   vtkId = elem->GetVtkID();
12219     if (!shapeIdToVtkIdSet.count(shapeId))
12220     {
12221       shapeIdToVtkIdSet[shapeId] = emptySet;
12222       shapeIds.insert(shapeId);
12223     }
12224     shapeIdToVtkIdSet[shapeId].insert(vtkId);
12225   }
12226
12227   std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12228   std::set<DownIdType, DownIdCompare> emptyEdges;
12229   emptyEdges.clear();
12230
12231   std::map<int, std::set<int> >::iterator itShape =  shapeIdToVtkIdSet.begin();
12232   for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12233   {
12234     int shapeId = itShape->first;
12235     //MESSAGE(" --- Shape ID --- "<< shapeId);
12236     shapeIdToEdges[shapeId] = emptyEdges;
12237
12238     std::vector<int> nodesEdges;
12239
12240     std::set<int>::iterator its = itShape->second.begin();
12241     for (; its != itShape->second.end(); ++its)
12242     {
12243       int vtkId = *its;
12244       //MESSAGE("     " << vtkId);
12245       int neighborsVtkIds[NBMAXNEIGHBORS];
12246       int downIds[NBMAXNEIGHBORS];
12247       unsigned char downTypes[NBMAXNEIGHBORS];
12248       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12249       for (int n = 0; n < nbNeighbors; n++)
12250       {
12251         if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12252           continue;
12253         int smdsId = meshDS->FromVtkToSmds(neighborsVtkIds[n]);
12254         const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12255         if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12256         {
12257           DownIdType edge(downIds[n], downTypes[n]);
12258           if (!shapeIdToEdges[shapeId].count(edge))
12259           {
12260             shapeIdToEdges[shapeId].insert(edge);
12261             int vtkNodeId[3];
12262             int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12263             nodesEdges.push_back(vtkNodeId[0]);
12264             nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12265             //MESSAGE("       --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12266           }
12267         }
12268       }
12269     }
12270
12271     std::list<int> order;
12272     order.clear();
12273     if (nodesEdges.size() > 0)
12274     {
12275       order.push_back(nodesEdges[0]); //MESSAGE("       --- back " << order.back()+1); // SMDS id = VTK id + 1;
12276       nodesEdges[0] = -1;
12277       order.push_back(nodesEdges[1]); //MESSAGE("       --- back " << order.back()+1);
12278       nodesEdges[1] = -1; // do not reuse this edge
12279       bool found = true;
12280       while (found)
12281       {
12282         int nodeTofind = order.back(); // try first to push back
12283         int i = 0;
12284         for ( i = 0; i < (int)nodesEdges.size(); i++ )
12285           if (nodesEdges[i] == nodeTofind)
12286             break;
12287         if ( i == (int) nodesEdges.size() )
12288           found = false; // no follower found on back
12289         else
12290         {
12291           if (i%2) // odd ==> use the previous one
12292             if (nodesEdges[i-1] < 0)
12293               found = false;
12294             else
12295             {
12296               order.push_back(nodesEdges[i-1]); //MESSAGE("       --- back " << order.back()+1);
12297               nodesEdges[i-1] = -1;
12298             }
12299           else // even ==> use the next one
12300             if (nodesEdges[i+1] < 0)
12301               found = false;
12302             else
12303             {
12304               order.push_back(nodesEdges[i+1]); //MESSAGE("       --- back " << order.back()+1);
12305               nodesEdges[i+1] = -1;
12306             }
12307         }
12308         if (found)
12309           continue;
12310         // try to push front
12311         found = true;
12312         nodeTofind = order.front(); // try to push front
12313         for ( i = 0;  i < (int)nodesEdges.size(); i++ )
12314           if ( nodesEdges[i] == nodeTofind )
12315             break;
12316         if ( i == (int)nodesEdges.size() )
12317         {
12318           found = false; // no predecessor found on front
12319           continue;
12320         }
12321         if (i%2) // odd ==> use the previous one
12322           if (nodesEdges[i-1] < 0)
12323             found = false;
12324           else
12325           {
12326             order.push_front(nodesEdges[i-1]); //MESSAGE("       --- front " << order.front()+1);
12327             nodesEdges[i-1] = -1;
12328           }
12329         else // even ==> use the next one
12330           if (nodesEdges[i+1] < 0)
12331             found = false;
12332           else
12333           {
12334             order.push_front(nodesEdges[i+1]); //MESSAGE("       --- front " << order.front()+1);
12335             nodesEdges[i+1] = -1;
12336           }
12337       }
12338     }
12339
12340
12341     std::vector<int> nodes;
12342     nodes.push_back(shapeId);
12343     std::list<int>::iterator itl = order.begin();
12344     for (; itl != order.end(); itl++)
12345     {
12346       nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12347       //MESSAGE("              ordered node " << nodes[nodes.size()-1]);
12348     }
12349     listOfListOfNodes.push_back(nodes);
12350   }
12351
12352   //     partition geom faces with blocFissure
12353   //     mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12354   //     mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12355
12356   return;
12357 }
12358
12359
12360 //================================================================================
12361 /*!
12362  * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12363  * The created 2D mesh elements based on nodes of free faces of boundary volumes
12364  * \return TRUE if operation has been completed successfully, FALSE otherwise
12365  */
12366 //================================================================================
12367
12368 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12369 {
12370   // iterates on volume elements and detect all free faces on them
12371   SMESHDS_Mesh* aMesh = GetMeshDS();
12372   if (!aMesh)
12373     return false;
12374
12375   ElemFeatures faceType( SMDSAbs_Face );
12376   int nbFree = 0, nbExisted = 0, nbCreated = 0;
12377   SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12378   while(vIt->more())
12379   {
12380     const SMDS_MeshVolume* volume = vIt->next();
12381     SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12382     vTool.SetExternalNormal();
12383     const int iQuad = volume->IsQuadratic();
12384     faceType.SetQuad( iQuad );
12385     for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12386     {
12387       if (!vTool.IsFreeFace(iface))
12388         continue;
12389       nbFree++;
12390       vector<const SMDS_MeshNode *> nodes;
12391       int nbFaceNodes = vTool.NbFaceNodes(iface);
12392       const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12393       int inode = 0;
12394       for ( ; inode < nbFaceNodes; inode += iQuad+1)
12395         nodes.push_back(faceNodes[inode]);
12396
12397       if (iQuad) // add medium nodes
12398       {
12399         for ( inode = 1; inode < nbFaceNodes; inode += 2)
12400           nodes.push_back(faceNodes[inode]);
12401         if ( nbFaceNodes == 9 ) // bi-quadratic quad
12402           nodes.push_back(faceNodes[8]);
12403       }
12404       // add new face based on volume nodes
12405       if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) )
12406       {
12407         nbExisted++; // face already exists
12408       }
12409       else
12410       {
12411         AddElement( nodes, faceType.SetPoly( nbFaceNodes/(iQuad+1) > 4 ));
12412         nbCreated++;
12413       }
12414     }
12415   }
12416   return ( nbFree == ( nbExisted + nbCreated ));
12417 }
12418
12419 namespace
12420 {
12421   inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12422   {
12423     if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12424       return n;
12425     return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12426   }
12427 }
12428 //================================================================================
12429 /*!
12430  * \brief Creates missing boundary elements
12431  *  \param elements - elements whose boundary is to be checked
12432  *  \param dimension - defines type of boundary elements to create
12433  *  \param group - a group to store created boundary elements in
12434  *  \param targetMesh - a mesh to store created boundary elements in
12435  *  \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12436  *  \param toCopyExistingBoundary - if true, not only new but also pre-existing
12437  *                                boundary elements will be copied into the targetMesh
12438  *  \param toAddExistingBondary - if true, not only new but also pre-existing
12439  *                                boundary elements will be added into the new group
12440  *  \param aroundElements - if true, elements will be created on boundary of given
12441  *                          elements else, on boundary of the whole mesh.
12442  * \return nb of added boundary elements
12443  */
12444 //================================================================================
12445
12446 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12447                                        Bnd_Dimension           dimension,
12448                                        SMESH_Group*            group/*=0*/,
12449                                        SMESH_Mesh*             targetMesh/*=0*/,
12450                                        bool                    toCopyElements/*=false*/,
12451                                        bool                    toCopyExistingBoundary/*=false*/,
12452                                        bool                    toAddExistingBondary/*= false*/,
12453                                        bool                    aroundElements/*= false*/)
12454 {
12455   SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12456   SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12457   // hope that all elements are of the same type, do not check them all
12458   if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12459     throw SALOME_Exception(LOCALIZED("wrong element type"));
12460
12461   if ( !targetMesh )
12462     toCopyElements = toCopyExistingBoundary = false;
12463
12464   SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12465   SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12466   int nbAddedBnd = 0;
12467
12468   // editor adding present bnd elements and optionally holding elements to add to the group
12469   SMESH_MeshEditor* presentEditor;
12470   SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12471   presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12472
12473   SMESH_MesherHelper helper( *myMesh );
12474   const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12475   SMDS_VolumeTool vTool;
12476   TIDSortedElemSet avoidSet;
12477   const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12478   size_t inode;
12479
12480   typedef vector<const SMDS_MeshNode*> TConnectivity;
12481   TConnectivity tgtNodes;
12482   ElemFeatures elemKind( missType ), elemToCopy;
12483
12484   vector<const SMDS_MeshElement*> presentBndElems;
12485   vector<TConnectivity>           missingBndElems;
12486   vector<int>                     freeFacets;
12487   TConnectivity nodes, elemNodes;
12488
12489   SMDS_ElemIteratorPtr eIt;
12490   if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12491   else                  eIt = SMESHUtils::elemSetIterator( elements );
12492
12493   while ( eIt->more() )
12494   {
12495     const SMDS_MeshElement* elem = eIt->next();
12496     const int              iQuad = elem->IsQuadratic();
12497     elemKind.SetQuad( iQuad );
12498
12499     // ------------------------------------------------------------------------------------
12500     // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12501     // ------------------------------------------------------------------------------------
12502     presentBndElems.clear();
12503     missingBndElems.clear();
12504     freeFacets.clear(); nodes.clear(); elemNodes.clear();
12505     if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12506     {
12507       const SMDS_MeshElement* otherVol = 0;
12508       for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12509       {
12510         if ( !vTool.IsFreeFace(iface, &otherVol) &&
12511              ( !aroundElements || elements.count( otherVol )))
12512           continue;
12513         freeFacets.push_back( iface );
12514       }
12515       if ( missType == SMDSAbs_Face )
12516         vTool.SetExternalNormal();
12517       for ( size_t i = 0; i < freeFacets.size(); ++i )
12518       {
12519         int                iface = freeFacets[i];
12520         const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12521         const size_t nbFaceNodes = vTool.NbFaceNodes (iface);
12522         if ( missType == SMDSAbs_Edge ) // boundary edges
12523         {
12524           nodes.resize( 2+iQuad );
12525           for ( size_t i = 0; i < nbFaceNodes; i += 1+iQuad )
12526           {
12527             for ( size_t j = 0; j < nodes.size(); ++j )
12528               nodes[ j ] = nn[ i+j ];
12529             if ( const SMDS_MeshElement* edge =
12530                  aMesh->FindElement( nodes, SMDSAbs_Edge, /*noMedium=*/false ))
12531               presentBndElems.push_back( edge );
12532             else
12533               missingBndElems.push_back( nodes );
12534           }
12535         }
12536         else // boundary face
12537         {
12538           nodes.clear();
12539           for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12540             nodes.push_back( nn[inode] ); // add corner nodes
12541           if (iQuad)
12542             for ( inode = 1; inode < nbFaceNodes; inode += 2)
12543               nodes.push_back( nn[inode] ); // add medium nodes
12544           int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12545           if ( iCenter > 0 )
12546             nodes.push_back( vTool.GetNodes()[ iCenter ] );
12547
12548           if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12549                                                                SMDSAbs_Face, /*noMedium=*/false ))
12550             presentBndElems.push_back( f );
12551           else
12552             missingBndElems.push_back( nodes );
12553
12554           if ( targetMesh != myMesh )
12555           {
12556             // add 1D elements on face boundary to be added to a new mesh
12557             const SMDS_MeshElement* edge;
12558             for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12559             {
12560               if ( iQuad )
12561                 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12562               else
12563                 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12564               if ( edge && avoidSet.insert( edge ).second )
12565                 presentBndElems.push_back( edge );
12566             }
12567           }
12568         }
12569       }
12570     }
12571     else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12572     {
12573       avoidSet.clear(), avoidSet.insert( elem );
12574       elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesIterator() ),
12575                         SMDS_MeshElement::iterator() );
12576       elemNodes.push_back( elemNodes[0] );
12577       nodes.resize( 2 + iQuad );
12578       const int nbLinks = elem->NbCornerNodes();
12579       for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12580       {
12581         nodes[0] = elemNodes[iN];
12582         nodes[1] = elemNodes[iN+1+iQuad];
12583         if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12584           continue; // not free link
12585
12586         if ( iQuad ) nodes[2] = elemNodes[iN+1];
12587         if ( const SMDS_MeshElement* edge =
12588              aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12589           presentBndElems.push_back( edge );
12590         else
12591           missingBndElems.push_back( nodes );
12592       }
12593     }
12594
12595     // ---------------------------------
12596     // 2. Add missing boundary elements
12597     // ---------------------------------
12598     if ( targetMesh != myMesh )
12599       // instead of making a map of nodes in this mesh and targetMesh,
12600       // we create nodes with same IDs.
12601       for ( size_t i = 0; i < missingBndElems.size(); ++i )
12602       {
12603         TConnectivity& srcNodes = missingBndElems[i];
12604         tgtNodes.resize( srcNodes.size() );
12605         for ( inode = 0; inode < srcNodes.size(); ++inode )
12606           tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12607         if ( /*aroundElements && */tgtEditor.GetMeshDS()->FindElement( tgtNodes,
12608                                                                        missType,
12609                                                                        /*noMedium=*/false))
12610           continue;
12611         tgtEditor.AddElement( tgtNodes, elemKind.SetPoly( tgtNodes.size()/(iQuad+1) > 4 ));
12612         ++nbAddedBnd;
12613       }
12614     else
12615       for ( size_t i = 0; i < missingBndElems.size(); ++i )
12616       {
12617         TConnectivity& nodes = missingBndElems[ i ];
12618         if ( /*aroundElements && */tgtEditor.GetMeshDS()->FindElement( nodes,
12619                                                                        missType,
12620                                                                        /*noMedium=*/false))
12621           continue;
12622         SMDS_MeshElement* newElem =
12623           tgtEditor.AddElement( nodes, elemKind.SetPoly( nodes.size()/(iQuad+1) > 4 ));
12624         nbAddedBnd += bool( newElem );
12625
12626         // try to set a new element to a shape
12627         if ( myMesh->HasShapeToMesh() )
12628         {
12629           bool ok = true;
12630           set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12631           const size_t nbN = nodes.size() / (iQuad+1 );
12632           for ( inode = 0; inode < nbN && ok; ++inode )
12633           {
12634             pair<int, TopAbs_ShapeEnum> i_stype =
12635               helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12636             if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12637               mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12638           }
12639           if ( ok && mediumShapes.size() > 1 )
12640           {
12641             set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12642             pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12643             for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12644             {
12645               if (( ok = ( stype_i->first != stype_i_0.first )))
12646                 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12647                                         aMesh->IndexToShape( stype_i_0.second ));
12648             }
12649           }
12650           if ( ok && mediumShapes.begin()->first == missShapeType )
12651             aMesh->SetMeshElementOnShape( newElem, mediumShapes.begin()->second );
12652         }
12653       }
12654
12655     // ----------------------------------
12656     // 3. Copy present boundary elements
12657     // ----------------------------------
12658     if ( toCopyExistingBoundary )
12659       for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12660       {
12661         const SMDS_MeshElement* e = presentBndElems[i];
12662         tgtNodes.resize( e->NbNodes() );
12663         for ( inode = 0; inode < tgtNodes.size(); ++inode )
12664           tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12665         presentEditor->AddElement( tgtNodes, elemToCopy.Init( e ));
12666       }
12667     else // store present elements to add them to a group
12668       for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12669       {
12670         presentEditor->myLastCreatedElems.push_back( presentBndElems[ i ]);
12671       }
12672
12673   } // loop on given elements
12674
12675   // ---------------------------------------------
12676   // 4. Fill group with boundary elements
12677   // ---------------------------------------------
12678   if ( group )
12679   {
12680     if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12681       for ( size_t i = 0; i < tgtEditor.myLastCreatedElems.size(); ++i )
12682         g->SMDSGroup().Add( tgtEditor.myLastCreatedElems[ i ]);
12683   }
12684   tgtEditor.myLastCreatedElems.clear();
12685   tgtEditor2.myLastCreatedElems.clear();
12686
12687   // -----------------------
12688   // 5. Copy given elements
12689   // -----------------------
12690   if ( toCopyElements && targetMesh != myMesh )
12691   {
12692     if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12693     else                  eIt = SMESHUtils::elemSetIterator( elements );
12694     while (eIt->more())
12695     {
12696       const SMDS_MeshElement* elem = eIt->next();
12697       tgtNodes.resize( elem->NbNodes() );
12698       for ( inode = 0; inode < tgtNodes.size(); ++inode )
12699         tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12700       tgtEditor.AddElement( tgtNodes, elemToCopy.Init( elem ));
12701
12702       tgtEditor.myLastCreatedElems.clear();
12703     }
12704   }
12705   return nbAddedBnd;
12706 }
12707
12708 //================================================================================
12709 /*!
12710  * \brief Copy node position and set \a to node on the same geometry
12711  */
12712 //================================================================================
12713
12714 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
12715                                      const SMDS_MeshNode* to )
12716 {
12717   if ( !from || !to ) return;
12718
12719   SMDS_PositionPtr pos = from->GetPosition();
12720   if ( !pos || from->getshapeId() < 1 ) return;
12721
12722   switch ( pos->GetTypeOfPosition() )
12723   {
12724   case SMDS_TOP_3DSPACE: break;
12725
12726   case SMDS_TOP_FACE:
12727   {
12728     SMDS_FacePositionPtr fPos = pos;
12729     GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
12730                                 fPos->GetUParameter(), fPos->GetVParameter() );
12731     break;
12732   }
12733   case SMDS_TOP_EDGE:
12734   {
12735     // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
12736     SMDS_EdgePositionPtr ePos = pos;
12737     GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
12738     break;
12739   }
12740   case SMDS_TOP_VERTEX:
12741   {
12742     GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
12743     break;
12744   }
12745   case SMDS_TOP_UNSPEC:
12746   default:;
12747   }
12748 }