Salome HOME
Merge branch 'V9_6_BR'
[modules/smesh.git] / src / SMESH / SMESH_MeshEditor.cxx
1 // Copyright (C) 2007-2020  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     bool hasFacet( const TTriangleFacet& facet ) const
1812     {
1813       if ( _nbCorners == 4 )
1814       {
1815         const int* tetConn = _connectivity;
1816         for ( ; tetConn[0] >= 0; tetConn += 4 )
1817           if (( facet.contains( tetConn[0] ) +
1818                 facet.contains( tetConn[1] ) +
1819                 facet.contains( tetConn[2] ) +
1820                 facet.contains( tetConn[3] )) == 3 )
1821             return true;
1822       }
1823       else // prism, _nbCorners == 6
1824       {
1825         const int* prismConn = _connectivity;
1826         for ( ; prismConn[0] >= 0; prismConn += 6 )
1827         {
1828           if (( facet.contains( prismConn[0] ) &&
1829                 facet.contains( prismConn[1] ) &&
1830                 facet.contains( prismConn[2] ))
1831               ||
1832               ( facet.contains( prismConn[3] ) &&
1833                 facet.contains( prismConn[4] ) &&
1834                 facet.contains( prismConn[5] )))
1835             return true;
1836         }
1837       }
1838       return false;
1839     }
1840   };
1841
1842   //=======================================================================
1843   /*!
1844    * \brief return TSplitMethod for the given element to split into tetrahedra
1845    */
1846   //=======================================================================
1847
1848   TSplitMethod getTetraSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1849   {
1850     const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1851
1852     // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1853     // an edge and a face barycenter; tertaherdons are based on triangles and
1854     // a volume barycenter
1855     const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1856
1857     // Find out how adjacent volumes are split
1858
1859     vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1860     int hasAdjacentSplits = 0, maxTetConnSize = 0;
1861     for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1862     {
1863       int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1864       maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1865       if ( nbNodes < 4 ) continue;
1866
1867       list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1868       const int* nInd = vol.GetFaceNodesIndices( iF );
1869       if ( nbNodes == 4 )
1870       {
1871         TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1872         TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1873         if      ( t012.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t012 );
1874         else if ( t123.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t123 );
1875       }
1876       else
1877       {
1878         int iCom = 0; // common node of triangle faces to split into
1879         for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1880         {
1881           TTriangleFacet t012( nInd[ iQ * ( iCom             )],
1882                                nInd[ iQ * ( (iCom+1)%nbNodes )],
1883                                nInd[ iQ * ( (iCom+2)%nbNodes )]);
1884           TTriangleFacet t023( nInd[ iQ * ( iCom             )],
1885                                nInd[ iQ * ( (iCom+2)%nbNodes )],
1886                                nInd[ iQ * ( (iCom+3)%nbNodes )]);
1887           if ( t012.hasAdjacentVol( vol.Element() ) && t023.hasAdjacentVol( vol.Element() ))
1888           {
1889             triaSplits.push_back( t012 );
1890             triaSplits.push_back( t023 );
1891             break;
1892           }
1893         }
1894       }
1895       if ( !triaSplits.empty() )
1896         hasAdjacentSplits = true;
1897     }
1898
1899     // Among variants of split method select one compliant with adjacent volumes
1900
1901     TSplitMethod method;
1902     if ( !vol.Element()->IsPoly() && !is24TetMode )
1903     {
1904       int nbVariants = 2, nbTet = 0;
1905       const int** connVariants = 0;
1906       switch ( vol.Element()->GetEntityType() )
1907       {
1908       case SMDSEntity_Hexa:
1909       case SMDSEntity_Quad_Hexa:
1910       case SMDSEntity_TriQuad_Hexa:
1911         if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1912           connVariants = theHexTo5, nbTet = 5;
1913         else
1914           connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1915         break;
1916       case SMDSEntity_Pyramid:
1917       case SMDSEntity_Quad_Pyramid:
1918         connVariants = thePyraTo2;  nbTet = 2;
1919         break;
1920       case SMDSEntity_Penta:
1921       case SMDSEntity_Quad_Penta:
1922       case SMDSEntity_BiQuad_Penta:
1923         connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1924         break;
1925       default:
1926         nbVariants = 0;
1927       }
1928       for ( int variant = 0; variant < nbVariants && method._nbSplits == 0; ++variant )
1929       {
1930         // check method compliancy with adjacent tetras,
1931         // all found splits must be among facets of tetras described by this method
1932         method = TSplitMethod( nbTet, connVariants[variant] );
1933         if ( hasAdjacentSplits && method._nbSplits > 0 )
1934         {
1935           bool facetCreated = true;
1936           for ( size_t iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1937           {
1938             list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1939             for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1940               facetCreated = method.hasFacet( *facet );
1941           }
1942           if ( !facetCreated )
1943             method = TSplitMethod(0); // incompatible method
1944         }
1945       }
1946     }
1947     if ( method._nbSplits < 1 )
1948     {
1949       // No standard method is applicable, use a generic solution:
1950       // each facet of a volume is split into triangles and
1951       // each of triangles and a volume barycenter form a tetrahedron.
1952
1953       const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1954
1955       int* connectivity = new int[ maxTetConnSize + 1 ];
1956       method._connectivity = connectivity;
1957       method._ownConn = true;
1958       method._baryNode = !isHex27; // to create central node or not
1959
1960       int connSize = 0;
1961       int baryCenInd = vol.NbNodes() - int( isHex27 );
1962       for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1963       {
1964         const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1965         const int*   nInd = vol.GetFaceNodesIndices( iF );
1966         // find common node of triangle facets of tetra to create
1967         int iCommon = 0; // index in linear numeration
1968         const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1969         if ( !triaSplits.empty() )
1970         {
1971           // by found facets
1972           const TTriangleFacet* facet = &triaSplits.front();
1973           for ( ; iCommon < nbNodes-1 ; ++iCommon )
1974             if ( facet->contains( nInd[ iQ * iCommon ]) &&
1975                  facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1976               break;
1977         }
1978         else if ( nbNodes > 3 && !is24TetMode )
1979         {
1980           // find the best method of splitting into triangles by aspect ratio
1981           SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1982           map< double, int > badness2iCommon;
1983           const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1984           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1985           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1986           {
1987             double badness = 0;
1988             for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1989             {
1990               SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon         )],
1991                                       nodes[ iQ*((iLast-1)%nbNodes)],
1992                                       nodes[ iQ*((iLast  )%nbNodes)]);
1993               badness += getBadRate( &tria, aspectRatio );
1994             }
1995             badness2iCommon.insert( make_pair( badness, iCommon ));
1996           }
1997           // use iCommon with lowest badness
1998           iCommon = badness2iCommon.begin()->second;
1999         }
2000         if ( iCommon >= nbNodes )
2001           iCommon = 0; // something wrong
2002
2003         // fill connectivity of tetrahedra based on a current face
2004         int nbTet = nbNodes - 2;
2005         if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
2006         {
2007           int faceBaryCenInd;
2008           if ( isHex27 )
2009           {
2010             faceBaryCenInd = vol.GetCenterNodeIndex( iF );
2011             method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
2012           }
2013           else
2014           {
2015             method._faceBaryNode[ iF ] = 0;
2016             faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
2017           }
2018           nbTet = nbNodes;
2019           for ( int i = 0; i < nbTet; ++i )
2020           {
2021             int i1 = i, i2 = (i+1) % nbNodes;
2022             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2023             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2024             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2025             connectivity[ connSize++ ] = faceBaryCenInd;
2026             connectivity[ connSize++ ] = baryCenInd;
2027           }
2028         }
2029         else
2030         {
2031           for ( int i = 0; i < nbTet; ++i )
2032           {
2033             int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
2034             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2035             connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
2036             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2037             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2038             connectivity[ connSize++ ] = baryCenInd;
2039           }
2040         }
2041         method._nbSplits += nbTet;
2042
2043       } // loop on volume faces
2044
2045       connectivity[ connSize++ ] = -1;
2046
2047     } // end of generic solution
2048
2049     return method;
2050   }
2051   //=======================================================================
2052   /*!
2053    * \brief return TSplitMethod to split haxhedron into prisms
2054    */
2055   //=======================================================================
2056
2057   TSplitMethod getPrismSplitMethod( SMDS_VolumeTool& vol,
2058                                     const int        methodFlags,
2059                                     const int        facetToSplit)
2060   {
2061     // order of facets in HEX according to SMDS_VolumeTool::Hexa_F :
2062     // B, T, L, B, R, F
2063     const int iF = ( facetToSplit < 2 ) ? 0 : 1 + ( facetToSplit-2 ) % 2; // [0,1,2]
2064
2065     if ( methodFlags == SMESH_MeshEditor::HEXA_TO_4_PRISMS )
2066     {
2067       static TSplitMethod to4methods[4]; // order BT, LR, FB
2068       if ( to4methods[iF]._nbSplits == 0 )
2069       {
2070         switch ( iF ) {
2071         case 0:
2072           to4methods[iF]._connectivity = theHexTo4Prisms_BT;
2073           to4methods[iF]._faceBaryNode[ 0 ] = 0;
2074           to4methods[iF]._faceBaryNode[ 1 ] = 0;
2075           break;
2076         case 1:
2077           to4methods[iF]._connectivity = theHexTo4Prisms_LR;
2078           to4methods[iF]._faceBaryNode[ 2 ] = 0;
2079           to4methods[iF]._faceBaryNode[ 4 ] = 0;
2080           break;
2081         case 2:
2082           to4methods[iF]._connectivity = theHexTo4Prisms_FB;
2083           to4methods[iF]._faceBaryNode[ 3 ] = 0;
2084           to4methods[iF]._faceBaryNode[ 5 ] = 0;
2085           break;
2086         default: return to4methods[3];
2087         }
2088         to4methods[iF]._nbSplits  = 4;
2089         to4methods[iF]._nbCorners = 6;
2090       }
2091       return to4methods[iF];
2092     }
2093     // else if ( methodFlags == HEXA_TO_2_PRISMS )
2094
2095     TSplitMethod method;
2096
2097     const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2098
2099     const int nbVariants = 2, nbSplits = 2;
2100     const int** connVariants = 0;
2101     switch ( iF ) {
2102     case 0: connVariants = theHexTo2Prisms_BT; break;
2103     case 1: connVariants = theHexTo2Prisms_LR; break;
2104     case 2: connVariants = theHexTo2Prisms_FB; break;
2105     default: return method;
2106     }
2107
2108     // look for prisms adjacent via facetToSplit and an opposite one
2109     for ( int is2nd = 0; is2nd < 2; ++is2nd )
2110     {
2111       int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2112       int nbNodes = vol.NbFaceNodes( iFacet ) / iQ;
2113       if ( nbNodes != 4 ) return method;
2114
2115       const int* nInd = vol.GetFaceNodesIndices( iFacet );
2116       TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2117       TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2118       TTriangleFacet* t;
2119       if      ( t012.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2120         t = &t012;
2121       else if ( t123.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2122         t = &t123;
2123       else
2124         continue;
2125
2126       // there are adjacent prism
2127       for ( int variant = 0; variant < nbVariants; ++variant )
2128       {
2129         // check method compliancy with adjacent prisms,
2130         // the found prism facets must be among facets of prisms described by current method
2131         method._nbSplits     = nbSplits;
2132         method._nbCorners    = 6;
2133         method._connectivity = connVariants[ variant ];
2134         if ( method.hasFacet( *t ))
2135           return method;
2136       }
2137     }
2138
2139     // No adjacent prisms. Select a variant with a best aspect ratio.
2140
2141     double badness[2] = { 0., 0. };
2142     static SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
2143     const SMDS_MeshNode** nodes = vol.GetNodes();
2144     for ( int variant = 0; variant < nbVariants; ++variant )
2145       for ( int is2nd = 0; is2nd < 2; ++is2nd )
2146       {
2147         int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2148         const int*             nInd = vol.GetFaceNodesIndices( iFacet );
2149
2150         method._connectivity = connVariants[ variant ];
2151         TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2152         TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2153         TTriangleFacet* t = ( method.hasFacet( t012 )) ? & t012 : & t123;
2154
2155         SMDS_FaceOfNodes tria ( nodes[ t->_n1 ],
2156                                 nodes[ t->_n2 ],
2157                                 nodes[ t->_n3 ] );
2158         badness[ variant ] += getBadRate( &tria, aspectRatio );
2159       }
2160     const int iBetter = ( badness[1] < badness[0] && badness[0]-badness[1] > 0.1 * badness[0] );
2161
2162     method._nbSplits     = nbSplits;
2163     method._nbCorners    = 6;
2164     method._connectivity = connVariants[ iBetter ];
2165
2166     return method;
2167   }
2168
2169   //================================================================================
2170   /*!
2171    * \brief Check if there is a tetraherdon adjacent to the given element via this facet
2172    */
2173   //================================================================================
2174
2175   bool TTriangleFacet::hasAdjacentVol( const SMDS_MeshElement*    elem,
2176                                        const SMDSAbs_GeometryType geom ) const
2177   {
2178     // find the tetrahedron including the three nodes of facet
2179     const SMDS_MeshNode* n1 = elem->GetNode(_n1);
2180     const SMDS_MeshNode* n2 = elem->GetNode(_n2);
2181     const SMDS_MeshNode* n3 = elem->GetNode(_n3);
2182     SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
2183     while ( volIt1->more() )
2184     {
2185       const SMDS_MeshElement* v = volIt1->next();
2186       if ( v->GetGeomType() != geom )
2187         continue;
2188       const int lastCornerInd = v->NbCornerNodes() - 1;
2189       if ( v->IsQuadratic() && v->GetNodeIndex( n1 ) > lastCornerInd )
2190         continue; // medium node not allowed
2191       const int ind2 = v->GetNodeIndex( n2 );
2192       if ( ind2 < 0 || lastCornerInd < ind2 )
2193         continue;
2194       const int ind3 = v->GetNodeIndex( n3 );
2195       if ( ind3 < 0 || lastCornerInd < ind3 )
2196         continue;
2197       return true;
2198     }
2199     return false;
2200   }
2201
2202   //=======================================================================
2203   /*!
2204    * \brief A key of a face of volume
2205    */
2206   //=======================================================================
2207
2208   struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
2209   {
2210     TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
2211     {
2212       TIDSortedNodeSet sortedNodes;
2213       const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2214       int nbNodes = vol.NbFaceNodes( iF );
2215       const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
2216       for ( int i = 0; i < nbNodes; i += iQ )
2217         sortedNodes.insert( fNodes[i] );
2218       TIDSortedNodeSet::iterator n = sortedNodes.begin();
2219       first.first   = (*(n++))->GetID();
2220       first.second  = (*(n++))->GetID();
2221       second.first  = (*(n++))->GetID();
2222       second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
2223     }
2224   };
2225 } // namespace
2226
2227 //=======================================================================
2228 //function : SplitVolumes
2229 //purpose  : Split volume elements into tetrahedra or prisms.
2230 //           If facet ID < 0, element is split into tetrahedra,
2231 //           else a hexahedron is split into prisms so that the given facet is
2232 //           split into triangles
2233 //=======================================================================
2234
2235 void SMESH_MeshEditor::SplitVolumes (const TFacetOfElem & theElems,
2236                                      const int            theMethodFlags)
2237 {
2238   SMDS_VolumeTool    volTool;
2239   SMESH_MesherHelper helper( *GetMesh()), fHelper(*GetMesh());
2240   fHelper.ToFixNodeParameters( true );
2241
2242   SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
2243   SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
2244
2245   SMESH_SequenceOfElemPtr newNodes, newElems;
2246
2247   // map face of volume to it's baricenrtic node
2248   map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
2249   double bc[3];
2250   vector<const SMDS_MeshElement* > splitVols;
2251
2252   TFacetOfElem::const_iterator elem2facet = theElems.begin();
2253   for ( ; elem2facet != theElems.end(); ++elem2facet )
2254   {
2255     const SMDS_MeshElement* elem = elem2facet->first;
2256     const int       facetToSplit = elem2facet->second;
2257     if ( elem->GetType() != SMDSAbs_Volume )
2258       continue;
2259     const SMDSAbs_EntityType geomType = elem->GetEntityType();
2260     if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
2261       continue;
2262
2263     if ( !volTool.Set( elem, /*ignoreCentralNodes=*/false )) continue; // strange...
2264
2265     TSplitMethod splitMethod = ( facetToSplit < 0  ?
2266                                  getTetraSplitMethod( volTool, theMethodFlags ) :
2267                                  getPrismSplitMethod( volTool, theMethodFlags, facetToSplit ));
2268     if ( splitMethod._nbSplits < 1 ) continue;
2269
2270     // find submesh to add new tetras to
2271     if ( !subMesh || !subMesh->Contains( elem ))
2272     {
2273       int shapeID = FindShape( elem );
2274       helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
2275       subMesh = GetMeshDS()->MeshElements( shapeID );
2276     }
2277     int iQ;
2278     if ( elem->IsQuadratic() )
2279     {
2280       iQ = 2;
2281       // add quadratic links to the helper
2282       for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2283       {
2284         const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
2285         int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
2286         for ( int iN = 0; iN < nbN; iN += iQ )
2287           helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
2288       }
2289       helper.SetIsQuadratic( true );
2290     }
2291     else
2292     {
2293       iQ = 1;
2294       helper.SetIsQuadratic( false );
2295     }
2296     vector<const SMDS_MeshNode*> nodes( volTool.GetNodes(),
2297                                         volTool.GetNodes() + elem->NbNodes() );
2298     helper.SetElementsOnShape( true );
2299     if ( splitMethod._baryNode )
2300     {
2301       // make a node at barycenter
2302       volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
2303       SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
2304       nodes.push_back( gcNode );
2305       newNodes.push_back( gcNode );
2306     }
2307     if ( !splitMethod._faceBaryNode.empty() )
2308     {
2309       // make or find baricentric nodes of faces
2310       map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
2311       for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
2312       {
2313         map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
2314           volFace2BaryNode.insert
2315           ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
2316         if ( !f_n->second )
2317         {
2318           volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
2319           newNodes.push_back( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
2320         }
2321         nodes.push_back( iF_n->second = f_n->second );
2322       }
2323     }
2324
2325     // make new volumes
2326     splitVols.resize( splitMethod._nbSplits ); // splits of a volume
2327     const int* volConn = splitMethod._connectivity;
2328     if ( splitMethod._nbCorners == 4 ) // tetra
2329       for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2330         newElems.push_back( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2331                                                                nodes[ volConn[1] ],
2332                                                                nodes[ volConn[2] ],
2333                                                                nodes[ volConn[3] ]));
2334     else // prisms
2335       for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2336         newElems.push_back( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2337                                                                nodes[ volConn[1] ],
2338                                                                nodes[ volConn[2] ],
2339                                                                nodes[ volConn[3] ],
2340                                                                nodes[ volConn[4] ],
2341                                                                nodes[ volConn[5] ]));
2342
2343     ReplaceElemInGroups( elem, splitVols, GetMeshDS() );
2344
2345     // Split faces on sides of the split volume
2346
2347     const SMDS_MeshNode** volNodes = volTool.GetNodes();
2348     for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2349     {
2350       const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
2351       if ( nbNodes < 4 ) continue;
2352
2353       // find an existing face
2354       vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
2355                                            volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
2356       while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
2357                                                                        /*noMedium=*/false))
2358       {
2359         // make triangles
2360         helper.SetElementsOnShape( false );
2361         vector< const SMDS_MeshElement* > triangles;
2362
2363         // find submesh to add new triangles in
2364         if ( !fSubMesh || !fSubMesh->Contains( face ))
2365         {
2366           int shapeID = FindShape( face );
2367           fSubMesh = GetMeshDS()->MeshElements( shapeID );
2368         }
2369         map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
2370         if ( iF_n != splitMethod._faceBaryNode.end() )
2371         {
2372           const SMDS_MeshNode *baryNode = iF_n->second;
2373           for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
2374           {
2375             const SMDS_MeshNode* n1 = fNodes[iN];
2376             const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
2377             const SMDS_MeshNode *n3 = baryNode;
2378             if ( !volTool.IsFaceExternal( iF ))
2379               swap( n2, n3 );
2380             triangles.push_back( helper.AddFace( n1,n2,n3 ));
2381           }
2382           if ( fSubMesh ) // update position of the bary node on geometry
2383           {
2384             if ( subMesh )
2385               subMesh->RemoveNode( baryNode );
2386             GetMeshDS()->SetNodeOnFace( baryNode, fSubMesh->GetID() );
2387             const TopoDS_Shape& s = GetMeshDS()->IndexToShape( fSubMesh->GetID() );
2388             if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE )
2389             {
2390               fHelper.SetSubShape( s );
2391               gp_XY uv( 1e100, 1e100 );
2392               double distXYZ[4];
2393               if ( !fHelper.CheckNodeUV( TopoDS::Face( s ), baryNode,
2394                                          uv, /*tol=*/1e-7, /*force=*/true, distXYZ ) &&
2395                    uv.X() < 1e100 )
2396               {
2397                 // node is too far from the surface
2398                 GetMeshDS()->MoveNode( baryNode, distXYZ[1], distXYZ[2], distXYZ[3] );
2399                 const_cast<SMDS_MeshNode*>( baryNode )->SetPosition
2400                   ( SMDS_PositionPtr( new SMDS_FacePosition( uv.X(), uv.Y() )));
2401               }
2402             }
2403           }
2404         }
2405         else
2406         {
2407           // among possible triangles create ones described by split method
2408           const int* nInd = volTool.GetFaceNodesIndices( iF );
2409           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2410           int iCom = 0; // common node of triangle faces to split into
2411           list< TTriangleFacet > facets;
2412           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
2413           {
2414             TTriangleFacet t012( nInd[ iQ * ( iCom                )],
2415                                  nInd[ iQ * ( (iCom+1)%nbNodes )],
2416                                  nInd[ iQ * ( (iCom+2)%nbNodes )]);
2417             TTriangleFacet t023( nInd[ iQ * ( iCom                )],
2418                                  nInd[ iQ * ( (iCom+2)%nbNodes )],
2419                                  nInd[ iQ * ( (iCom+3)%nbNodes )]);
2420             if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
2421             {
2422               facets.push_back( t012 );
2423               facets.push_back( t023 );
2424               for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
2425                 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom             )],
2426                                                   nInd[ iQ * ((iLast-1)%nbNodes )],
2427                                                   nInd[ iQ * ((iLast  )%nbNodes )]));
2428               break;
2429             }
2430           }
2431           list< TTriangleFacet >::iterator facet = facets.begin();
2432           if ( facet == facets.end() )
2433             break;
2434           for ( ; facet != facets.end(); ++facet )
2435           {
2436             if ( !volTool.IsFaceExternal( iF ))
2437               swap( facet->_n2, facet->_n3 );
2438             triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
2439                                                  volNodes[ facet->_n2 ],
2440                                                  volNodes[ facet->_n3 ]));
2441           }
2442         }
2443         for ( size_t i = 0; i < triangles.size(); ++i )
2444         {
2445           if ( !triangles[ i ]) continue;
2446           if ( fSubMesh )
2447             fSubMesh->AddElement( triangles[ i ]);
2448           newElems.push_back( triangles[ i ]);
2449         }
2450         ReplaceElemInGroups( face, triangles, GetMeshDS() );
2451         GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2452
2453       } // while a face based on facet nodes exists
2454     } // loop on volume faces to split them into triangles
2455
2456     GetMeshDS()->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2457
2458     if ( geomType == SMDSEntity_TriQuad_Hexa )
2459     {
2460       // remove medium nodes that could become free
2461       for ( int i = 20; i < volTool.NbNodes(); ++i )
2462         if ( volNodes[i]->NbInverseElements() == 0 )
2463           GetMeshDS()->RemoveNode( volNodes[i] );
2464     }
2465   } // loop on volumes to split
2466
2467   myLastCreatedNodes = newNodes;
2468   myLastCreatedElems = newElems;
2469 }
2470
2471 //=======================================================================
2472 //function : GetHexaFacetsToSplit
2473 //purpose  : For hexahedra that will be split into prisms, finds facets to
2474 //           split into triangles. Only hexahedra adjacent to the one closest
2475 //           to theFacetNormal.Location() are returned.
2476 //param [in,out] theHexas - the hexahedra
2477 //param [in]     theFacetNormal - facet normal
2478 //param [out]    theFacets - the hexahedra and found facet IDs
2479 //=======================================================================
2480
2481 void SMESH_MeshEditor::GetHexaFacetsToSplit( TIDSortedElemSet& theHexas,
2482                                              const gp_Ax1&     theFacetNormal,
2483                                              TFacetOfElem &    theFacets)
2484 {
2485 #define THIS_METHOD "SMESH_MeshEditor::GetHexaFacetsToSplit(): "
2486
2487   // Find a hexa closest to the location of theFacetNormal
2488
2489   const SMDS_MeshElement* startHex;
2490   {
2491     // get SMDS_ElemIteratorPtr on theHexas
2492     typedef const SMDS_MeshElement*                                      TValue;
2493     typedef TIDSortedElemSet::iterator                                   TSetIterator;
2494     typedef SMDS::SimpleAccessor<TValue,TSetIterator>                    TAccesor;
2495     typedef SMDS_MeshElement::GeomFilter                                 TFilter;
2496     typedef SMDS_SetIterator < TValue, TSetIterator, TAccesor, TFilter > TElemSetIter;
2497     SMDS_ElemIteratorPtr elemIt = SMDS_ElemIteratorPtr
2498       ( new TElemSetIter( theHexas.begin(),
2499                           theHexas.end(),
2500                           SMDS_MeshElement::GeomFilter( SMDSGeom_HEXA )));
2501
2502     SMESH_ElementSearcher* searcher =
2503       SMESH_MeshAlgos::GetElementSearcher( *myMesh->GetMeshDS(), elemIt );
2504
2505     startHex = searcher->FindClosestTo( theFacetNormal.Location(), SMDSAbs_Volume );
2506
2507     delete searcher;
2508
2509     if ( !startHex )
2510       throw SALOME_Exception( THIS_METHOD "startHex not found");
2511   }
2512
2513   // Select a facet of startHex by theFacetNormal
2514
2515   SMDS_VolumeTool vTool( startHex );
2516   double norm[3], dot, maxDot = 0;
2517   int facetID = -1;
2518   for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2519     if ( vTool.GetFaceNormal( iF, norm[0], norm[1], norm[2] ))
2520     {
2521       dot = Abs( theFacetNormal.Direction().Dot( gp_Dir( norm[0], norm[1], norm[2] )));
2522       if ( dot > maxDot )
2523       {
2524         facetID = iF;
2525         maxDot = dot;
2526       }
2527     }
2528   if ( facetID < 0 )
2529     throw SALOME_Exception( THIS_METHOD "facet of startHex not found");
2530
2531   // Fill theFacets starting from facetID of startHex
2532
2533   // facets used for searching of volumes adjacent to already treated ones
2534   typedef pair< TFacetOfElem::iterator, int > TElemFacets;
2535   typedef map< TVolumeFaceKey, TElemFacets  > TFacetMap;
2536   TFacetMap facetsToCheck;
2537
2538   set<const SMDS_MeshNode*> facetNodes;
2539   const SMDS_MeshElement*   curHex;
2540
2541   const bool allHex = ((int) theHexas.size() == myMesh->NbHexas() );
2542
2543   while ( startHex )
2544   {
2545     // move in two directions from startHex via facetID
2546     for ( int is2nd = 0; is2nd < 2; ++is2nd )
2547     {
2548       curHex       = startHex;
2549       int curFacet = facetID;
2550       if ( is2nd ) // do not treat startHex twice
2551       {
2552         vTool.Set( curHex );
2553         if ( vTool.IsFreeFace( curFacet, &curHex ))
2554         {
2555           curHex = 0;
2556         }
2557         else
2558         {
2559           vTool.GetFaceNodes( curFacet, facetNodes );
2560           vTool.Set( curHex );
2561           curFacet = vTool.GetFaceIndex( facetNodes );
2562         }
2563       }
2564       while ( curHex )
2565       {
2566         // store a facet to split
2567         if ( curHex->GetGeomType() != SMDSGeom_HEXA )
2568         {
2569           theFacets.insert( make_pair( curHex, -1 ));
2570           break;
2571         }
2572         if ( !allHex && !theHexas.count( curHex ))
2573           break;
2574
2575         pair< TFacetOfElem::iterator, bool > facetIt2isNew =
2576           theFacets.insert( make_pair( curHex, curFacet ));
2577         if ( !facetIt2isNew.second )
2578           break;
2579
2580         // remember not-to-split facets in facetsToCheck
2581         int oppFacet = vTool.GetOppFaceIndexOfHex( curFacet );
2582         for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2583         {
2584           if ( iF == curFacet && iF == oppFacet )
2585             continue;
2586           TVolumeFaceKey facetKey ( vTool, iF );
2587           TElemFacets    elemFacet( facetIt2isNew.first, iF );
2588           pair< TFacetMap::iterator, bool > it2isnew =
2589             facetsToCheck.insert( make_pair( facetKey, elemFacet ));
2590           if ( !it2isnew.second )
2591             facetsToCheck.erase( it2isnew.first ); // adjacent hex already checked
2592         }
2593         // pass to a volume adjacent via oppFacet
2594         if ( vTool.IsFreeFace( oppFacet, &curHex ))
2595         {
2596           curHex = 0;
2597         }
2598         else
2599         {
2600           // get a new curFacet
2601           vTool.GetFaceNodes( oppFacet, facetNodes );
2602           vTool.Set( curHex );
2603           curFacet = vTool.GetFaceIndex( facetNodes, /*hint=*/curFacet );
2604         }
2605       }
2606     } // move in two directions from startHex via facetID
2607
2608     // Find a new startHex by facetsToCheck
2609
2610     startHex = 0;
2611     facetID  = -1;
2612     TFacetMap::iterator fIt = facetsToCheck.begin();
2613     while ( !startHex && fIt != facetsToCheck.end() )
2614     {
2615       const TElemFacets&  elemFacets = fIt->second;
2616       const SMDS_MeshElement*    hex = elemFacets.first->first;
2617       int                 splitFacet = elemFacets.first->second;
2618       int               lateralFacet = elemFacets.second;
2619       facetsToCheck.erase( fIt );
2620       fIt = facetsToCheck.begin();
2621
2622       vTool.Set( hex );
2623       if ( vTool.IsFreeFace( lateralFacet, &curHex ) || 
2624            curHex->GetGeomType() != SMDSGeom_HEXA )
2625         continue;
2626       if ( !allHex && !theHexas.count( curHex ))
2627         continue;
2628
2629       startHex = curHex;
2630
2631       // find a facet of startHex to split
2632
2633       set<const SMDS_MeshNode*> lateralNodes;
2634       vTool.GetFaceNodes( lateralFacet, lateralNodes );
2635       vTool.GetFaceNodes( splitFacet,   facetNodes );
2636       int oppLateralFacet = vTool.GetOppFaceIndexOfHex( lateralFacet );
2637       vTool.Set( startHex );
2638       lateralFacet = vTool.GetFaceIndex( lateralNodes, oppLateralFacet );
2639
2640       // look for a facet of startHex having common nodes with facetNodes
2641       // but not lateralFacet
2642       for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2643       {
2644         if ( iF == lateralFacet )
2645           continue;
2646         int nbCommonNodes = 0;
2647         const SMDS_MeshNode** nn = vTool.GetFaceNodes( iF );
2648         for ( int iN = 0, nbN = vTool.NbFaceNodes( iF ); iN < nbN; ++iN )
2649           nbCommonNodes += facetNodes.count( nn[ iN ]);
2650
2651         if ( nbCommonNodes >= 2 )
2652         {
2653           facetID = iF;
2654           break;
2655         }
2656       }
2657       if ( facetID < 0 )
2658         throw SALOME_Exception( THIS_METHOD "facet of a new startHex not found");
2659     }
2660   } //   while ( startHex )
2661
2662   return;
2663 }
2664
2665 namespace
2666 {
2667   //================================================================================
2668   /*!
2669    * \brief Selects nodes of several elements according to a given interlace
2670    *  \param [in] srcNodes - nodes to select from
2671    *  \param [out] tgtNodesVec - array of nodes of several elements to fill in
2672    *  \param [in] interlace - indices of nodes for all elements
2673    *  \param [in] nbElems - nb of elements
2674    *  \param [in] nbNodes - nb of nodes in each element
2675    *  \param [in] mesh - the mesh
2676    *  \param [out] elemQueue - a list to push elements found by the selected nodes
2677    *  \param [in] type - type of elements to look for
2678    */
2679   //================================================================================
2680
2681   void selectNodes( const vector< const SMDS_MeshNode* >& srcNodes,
2682                     vector< const SMDS_MeshNode* >*       tgtNodesVec,
2683                     const int*                            interlace,
2684                     const int                             nbElems,
2685                     const int                             nbNodes,
2686                     SMESHDS_Mesh*                         mesh = 0,
2687                     list< const SMDS_MeshElement* >*      elemQueue=0,
2688                     SMDSAbs_ElementType                   type=SMDSAbs_All)
2689   {
2690     for ( int iE = 0; iE < nbElems; ++iE )
2691     {
2692       vector< const SMDS_MeshNode* >& elemNodes = tgtNodesVec[iE];
2693       const int*                         select = & interlace[iE*nbNodes];
2694       elemNodes.resize( nbNodes );
2695       for ( int iN = 0; iN < nbNodes; ++iN )
2696         elemNodes[iN] = srcNodes[ select[ iN ]];
2697     }
2698     const SMDS_MeshElement* e;
2699     if ( elemQueue )
2700       for ( int iE = 0; iE < nbElems; ++iE )
2701         if (( e = mesh->FindElement( tgtNodesVec[iE], type, /*noMedium=*/false)))
2702           elemQueue->push_back( e );
2703   }
2704 }
2705
2706 //=======================================================================
2707 /*
2708  * Split bi-quadratic elements into linear ones without creation of additional nodes
2709  *   - bi-quadratic triangle will be split into 3 linear quadrangles;
2710  *   - bi-quadratic quadrangle will be split into 4 linear quadrangles;
2711  *   - tri-quadratic hexahedron will be split into 8 linear hexahedra;
2712  *   Quadratic elements of lower dimension  adjacent to the split bi-quadratic element
2713  *   will be split in order to keep the mesh conformal.
2714  *  \param elems - elements to split
2715  */
2716 //=======================================================================
2717
2718 void SMESH_MeshEditor::SplitBiQuadraticIntoLinear(TIDSortedElemSet& theElems)
2719 {
2720   vector< const SMDS_MeshNode* > elemNodes(27), subNodes[12], splitNodes[8];
2721   vector<const SMDS_MeshElement* > splitElems;
2722   list< const SMDS_MeshElement* > elemQueue;
2723   list< const SMDS_MeshElement* >::iterator elemIt;
2724
2725   SMESHDS_Mesh * mesh = GetMeshDS();
2726   ElemFeatures *elemType, hexaType(SMDSAbs_Volume), quadType(SMDSAbs_Face), segType(SMDSAbs_Edge);
2727   int nbElems, nbNodes;
2728
2729   TIDSortedElemSet::iterator elemSetIt = theElems.begin();
2730   for ( ; elemSetIt != theElems.end(); ++elemSetIt )
2731   {
2732     elemQueue.clear();
2733     elemQueue.push_back( *elemSetIt );
2734     for ( elemIt = elemQueue.begin(); elemIt != elemQueue.end(); ++elemIt )
2735     {
2736       const SMDS_MeshElement* elem = *elemIt;
2737       switch( elem->GetEntityType() )
2738       {
2739       case SMDSEntity_TriQuad_Hexa: // HEX27
2740       {
2741         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2742         nbElems  = nbNodes = 8;
2743         elemType = & hexaType;
2744
2745         // get nodes for new elements
2746         static int vInd[8][8] = {{ 0,8,20,11,   16,21,26,24 },
2747                                  { 1,9,20,8,    17,22,26,21 },
2748                                  { 2,10,20,9,   18,23,26,22 },
2749                                  { 3,11,20,10,  19,24,26,23 },
2750                                  { 16,21,26,24, 4,12,25,15  },
2751                                  { 17,22,26,21, 5,13,25,12  },
2752                                  { 18,23,26,22, 6,14,25,13  },
2753                                  { 19,24,26,23, 7,15,25,14  }};
2754         selectNodes( elemNodes, & splitNodes[0], &vInd[0][0], nbElems, nbNodes );
2755
2756         // add boundary faces to elemQueue
2757         static int fInd[6][9] = {{ 0,1,2,3, 8,9,10,11,   20 },
2758                                  { 4,5,6,7, 12,13,14,15, 25 },
2759                                  { 0,1,5,4, 8,17,12,16,  21 },
2760                                  { 1,2,6,5, 9,18,13,17,  22 },
2761                                  { 2,3,7,6, 10,19,14,18, 23 },
2762                                  { 3,0,4,7, 11,16,15,19, 24 }};
2763         selectNodes( elemNodes, & subNodes[0], &fInd[0][0], 6,9, mesh, &elemQueue, SMDSAbs_Face );
2764
2765         // add boundary segments to elemQueue
2766         static int eInd[12][3] = {{ 0,1,8 }, { 1,2,9 }, { 2,3,10 }, { 3,0,11 },
2767                                   { 4,5,12}, { 5,6,13}, { 6,7,14 }, { 7,4,15 },
2768                                   { 0,4,16}, { 1,5,17}, { 2,6,18 }, { 3,7,19 }};
2769         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 12,3, mesh, &elemQueue, SMDSAbs_Edge );
2770         break;
2771       }
2772       case SMDSEntity_BiQuad_Triangle: // TRIA7
2773       {
2774         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2775         nbElems = 3;
2776         nbNodes = 4;
2777         elemType = & quadType;
2778
2779         // get nodes for new elements
2780         static int fInd[3][4] = {{ 0,3,6,5 }, { 1,4,6,3 }, { 2,5,6,4 }};
2781         selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2782
2783         // add boundary segments to elemQueue
2784         static int eInd[3][3] = {{ 0,1,3 }, { 1,2,4 }, { 2,0,5 }};
2785         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 3,3, mesh, &elemQueue, SMDSAbs_Edge );
2786         break;
2787       }
2788       case SMDSEntity_BiQuad_Quadrangle: // QUAD9
2789       {
2790         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2791         nbElems = 4;
2792         nbNodes = 4;
2793         elemType = & quadType;
2794
2795         // get nodes for new elements
2796         static int fInd[4][4] = {{ 0,4,8,7 }, { 1,5,8,4 }, { 2,6,8,5 }, { 3,7,8,6 }};
2797         selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2798
2799         // add boundary segments to elemQueue
2800         static int eInd[4][3] = {{ 0,1,4 }, { 1,2,5 }, { 2,3,6 }, { 3,0,7 }};
2801         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 4,3, mesh, &elemQueue, SMDSAbs_Edge );
2802         break;
2803       }
2804       case SMDSEntity_Quad_Edge:
2805       {
2806         if ( elemIt == elemQueue.begin() )
2807           continue; // an elem is in theElems
2808         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2809         nbElems = 2;
2810         nbNodes = 2;
2811         elemType = & segType;
2812
2813         // get nodes for new elements
2814         static int eInd[2][2] = {{ 0,2 }, { 2,1 }};
2815         selectNodes( elemNodes, & splitNodes[0], &eInd[0][0], nbElems, nbNodes );
2816         break;
2817       }
2818       default: continue;
2819       } // switch( elem->GetEntityType() )
2820
2821       // Create new elements
2822
2823       SMESHDS_SubMesh* subMesh = mesh->MeshElements( elem->getshapeId() );
2824
2825       splitElems.clear();
2826
2827       //elemType->SetID( elem->GetID() ); // create an elem with the same ID as a removed one
2828       mesh->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2829       //splitElems.push_back( AddElement( splitNodes[ 0 ], *elemType ));
2830       //elemType->SetID( -1 );
2831
2832       for ( int iE = 0; iE < nbElems; ++iE )
2833         splitElems.push_back( AddElement( splitNodes[ iE ], *elemType ));
2834
2835
2836       ReplaceElemInGroups( elem, splitElems, mesh );
2837
2838       if ( subMesh )
2839         for ( size_t i = 0; i < splitElems.size(); ++i )
2840           subMesh->AddElement( splitElems[i] );
2841     }
2842   }
2843 }
2844
2845 //=======================================================================
2846 //function : AddToSameGroups
2847 //purpose  : add elemToAdd to the groups the elemInGroups belongs to
2848 //=======================================================================
2849
2850 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
2851                                         const SMDS_MeshElement* elemInGroups,
2852                                         SMESHDS_Mesh *          aMesh)
2853 {
2854   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2855   if (!groups.empty()) {
2856     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2857     for ( ; grIt != groups.end(); grIt++ ) {
2858       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2859       if ( group && group->Contains( elemInGroups ))
2860         group->SMDSGroup().Add( elemToAdd );
2861     }
2862   }
2863 }
2864
2865
2866 //=======================================================================
2867 //function : RemoveElemFromGroups
2868 //purpose  : Remove removeelem to the groups the elemInGroups belongs to
2869 //=======================================================================
2870 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2871                                              SMESHDS_Mesh *          aMesh)
2872 {
2873   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2874   if (!groups.empty())
2875   {
2876     set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2877     for (; GrIt != groups.end(); GrIt++)
2878     {
2879       SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2880       if (!grp || grp->IsEmpty()) continue;
2881       grp->SMDSGroup().Remove(removeelem);
2882     }
2883   }
2884 }
2885
2886 //================================================================================
2887 /*!
2888  * \brief Replace elemToRm by elemToAdd in the all groups
2889  */
2890 //================================================================================
2891
2892 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2893                                             const SMDS_MeshElement* elemToAdd,
2894                                             SMESHDS_Mesh *          aMesh)
2895 {
2896   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2897   if (!groups.empty()) {
2898     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2899     for ( ; grIt != groups.end(); grIt++ ) {
2900       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2901       if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2902         group->SMDSGroup().Add( elemToAdd );
2903     }
2904   }
2905 }
2906
2907 //================================================================================
2908 /*!
2909  * \brief Replace elemToRm by elemToAdd in the all groups
2910  */
2911 //================================================================================
2912
2913 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement*                elemToRm,
2914                                             const vector<const SMDS_MeshElement*>& elemToAdd,
2915                                             SMESHDS_Mesh *                         aMesh)
2916 {
2917   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2918   if (!groups.empty())
2919   {
2920     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2921     for ( ; grIt != groups.end(); grIt++ ) {
2922       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2923       if ( group && group->SMDSGroup().Remove( elemToRm ) )
2924         for ( size_t i = 0; i < elemToAdd.size(); ++i )
2925           group->SMDSGroup().Add( elemToAdd[ i ] );
2926     }
2927   }
2928 }
2929
2930 //=======================================================================
2931 //function : QuadToTri
2932 //purpose  : Cut quadrangles into triangles.
2933 //           theCrit is used to select a diagonal to cut
2934 //=======================================================================
2935
2936 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2937                                   const bool         the13Diag)
2938 {
2939   ClearLastCreated();
2940   myLastCreatedElems.reserve( theElems.size() * 2 );
2941
2942   SMESHDS_Mesh *       aMesh = GetMeshDS();
2943   Handle(Geom_Surface) surface;
2944   SMESH_MesherHelper   helper( *GetMesh() );
2945
2946   TIDSortedElemSet::iterator itElem;
2947   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
2948   {
2949     const SMDS_MeshElement* elem = *itElem;
2950     if ( !elem || elem->GetGeomType() != SMDSGeom_QUADRANGLE )
2951       continue;
2952
2953     if ( elem->NbNodes() == 4 ) {
2954       // retrieve element nodes
2955       const SMDS_MeshNode* aNodes [4];
2956       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2957       int i = 0;
2958       while ( itN->more() )
2959         aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2960
2961       int aShapeId = FindShape( elem );
2962       const SMDS_MeshElement* newElem1 = 0;
2963       const SMDS_MeshElement* newElem2 = 0;
2964       if ( the13Diag ) {
2965         newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2966         newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2967       }
2968       else {
2969         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2970         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2971       }
2972       myLastCreatedElems.push_back(newElem1);
2973       myLastCreatedElems.push_back(newElem2);
2974       // put a new triangle on the same shape and add to the same groups
2975       if ( aShapeId )
2976       {
2977         aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2978         aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2979       }
2980       AddToSameGroups( newElem1, elem, aMesh );
2981       AddToSameGroups( newElem2, elem, aMesh );
2982       aMesh->RemoveElement( elem );
2983     }
2984
2985     // Quadratic quadrangle
2986
2987     else if ( elem->NbNodes() >= 8 )
2988     {
2989       // get surface elem is on
2990       int aShapeId = FindShape( elem );
2991       if ( aShapeId != helper.GetSubShapeID() ) {
2992         surface.Nullify();
2993         TopoDS_Shape shape;
2994         if ( aShapeId > 0 )
2995           shape = aMesh->IndexToShape( aShapeId );
2996         if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
2997           TopoDS_Face face = TopoDS::Face( shape );
2998           surface = BRep_Tool::Surface( face );
2999           if ( !surface.IsNull() )
3000             helper.SetSubShape( shape );
3001         }
3002       }
3003
3004       const SMDS_MeshNode* aNodes [9]; aNodes[8] = 0;
3005       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3006       for ( int i = 0; itN->more(); ++i )
3007         aNodes[ i ] = static_cast<const SMDS_MeshNode*>( itN->next() );
3008
3009       const SMDS_MeshNode* centrNode = aNodes[8];
3010       if ( centrNode == 0 )
3011       {
3012         centrNode = helper.GetCentralNode( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3013                                            aNodes[4], aNodes[5], aNodes[6], aNodes[7],
3014                                            surface.IsNull() );
3015         myLastCreatedNodes.push_back(centrNode);
3016       }
3017
3018       // create a new element
3019       const SMDS_MeshElement* newElem1 = 0;
3020       const SMDS_MeshElement* newElem2 = 0;
3021       if ( the13Diag ) {
3022         newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
3023                                   aNodes[6], aNodes[7], centrNode );
3024         newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
3025                                   centrNode, aNodes[4], aNodes[5] );
3026       }
3027       else {
3028         newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
3029                                   aNodes[7], aNodes[4], centrNode );
3030         newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
3031                                   centrNode, aNodes[5], aNodes[6] );
3032       }
3033       myLastCreatedElems.push_back(newElem1);
3034       myLastCreatedElems.push_back(newElem2);
3035       // put a new triangle on the same shape and add to the same groups
3036       if ( aShapeId )
3037       {
3038         aMesh->SetMeshElementOnShape( newElem1, aShapeId );
3039         aMesh->SetMeshElementOnShape( newElem2, aShapeId );
3040       }
3041       AddToSameGroups( newElem1, elem, aMesh );
3042       AddToSameGroups( newElem2, elem, aMesh );
3043       aMesh->RemoveElement( elem );
3044     }
3045   }
3046
3047   return true;
3048 }
3049
3050 //=======================================================================
3051 //function : getAngle
3052 //purpose  :
3053 //=======================================================================
3054
3055 double getAngle(const SMDS_MeshElement * tr1,
3056                 const SMDS_MeshElement * tr2,
3057                 const SMDS_MeshNode *    n1,
3058                 const SMDS_MeshNode *    n2)
3059 {
3060   double angle = 2. * M_PI; // bad angle
3061
3062   // get normals
3063   SMESH::Controls::TSequenceOfXYZ P1, P2;
3064   if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
3065        !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
3066     return angle;
3067   gp_Vec N1,N2;
3068   if(!tr1->IsQuadratic())
3069     N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
3070   else
3071     N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
3072   if ( N1.SquareMagnitude() <= gp::Resolution() )
3073     return angle;
3074   if(!tr2->IsQuadratic())
3075     N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
3076   else
3077     N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
3078   if ( N2.SquareMagnitude() <= gp::Resolution() )
3079     return angle;
3080
3081   // find the first diagonal node n1 in the triangles:
3082   // take in account a diagonal link orientation
3083   const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
3084   for ( int t = 0; t < 2; t++ ) {
3085     SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
3086     int i = 0, iDiag = -1;
3087     while ( it->more()) {
3088       const SMDS_MeshElement *n = it->next();
3089       if ( n == n1 || n == n2 ) {
3090         if ( iDiag < 0)
3091           iDiag = i;
3092         else {
3093           if ( i - iDiag == 1 )
3094             nFirst[ t ] = ( n == n1 ? n2 : n1 );
3095           else
3096             nFirst[ t ] = n;
3097           break;
3098         }
3099       }
3100       i++;
3101     }
3102   }
3103   if ( nFirst[ 0 ] == nFirst[ 1 ] )
3104     N2.Reverse();
3105
3106   angle = N1.Angle( N2 );
3107   //SCRUTE( angle );
3108   return angle;
3109 }
3110
3111 // =================================================
3112 // class generating a unique ID for a pair of nodes
3113 // and able to return nodes by that ID
3114 // =================================================
3115 class LinkID_Gen {
3116 public:
3117
3118   LinkID_Gen( const SMESHDS_Mesh* theMesh )
3119     :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
3120   {}
3121
3122   long GetLinkID (const SMDS_MeshNode * n1,
3123                   const SMDS_MeshNode * n2) const
3124   {
3125     return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
3126   }
3127
3128   bool GetNodes (const long             theLinkID,
3129                  const SMDS_MeshNode* & theNode1,
3130                  const SMDS_MeshNode* & theNode2) const
3131   {
3132     theNode1 = myMesh->FindNode( theLinkID / myMaxID );
3133     if ( !theNode1 ) return false;
3134     theNode2 = myMesh->FindNode( theLinkID % myMaxID );
3135     if ( !theNode2 ) return false;
3136     return true;
3137   }
3138
3139 private:
3140   LinkID_Gen();
3141   const SMESHDS_Mesh* myMesh;
3142   long                myMaxID;
3143 };
3144
3145
3146 //=======================================================================
3147 //function : TriToQuad
3148 //purpose  : Fuse neighbour triangles into quadrangles.
3149 //           theCrit is used to select a neighbour to fuse with.
3150 //           theMaxAngle is a max angle between element normals at which
3151 //           fusion is still performed.
3152 //=======================================================================
3153
3154 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet &                   theElems,
3155                                   SMESH::Controls::NumericalFunctorPtr theCrit,
3156                                   const double                         theMaxAngle)
3157 {
3158   ClearLastCreated();
3159   myLastCreatedElems.reserve( theElems.size() / 2 );
3160
3161   if ( !theCrit.get() )
3162     return false;
3163
3164   SMESHDS_Mesh * aMesh = GetMeshDS();
3165
3166   // Prepare data for algo: build
3167   // 1. map of elements with their linkIDs
3168   // 2. map of linkIDs with their elements
3169
3170   map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
3171   map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
3172   map< const SMDS_MeshElement*, set< SMESH_TLink > >  mapEl_setLi;
3173   map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
3174
3175   TIDSortedElemSet::iterator itElem;
3176   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
3177   {
3178     const SMDS_MeshElement* elem = *itElem;
3179     if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
3180     bool IsTria = ( elem->NbCornerNodes()==3 );
3181     if (!IsTria) continue;
3182
3183     // retrieve element nodes
3184     const SMDS_MeshNode* aNodes [4];
3185     SMDS_NodeIteratorPtr itN = elem->nodeIterator();
3186     int i = 0;
3187     while ( i < 3 )
3188       aNodes[ i++ ] = itN->next();
3189     aNodes[ 3 ] = aNodes[ 0 ];
3190
3191     // fill maps
3192     for ( i = 0; i < 3; i++ ) {
3193       SMESH_TLink link( aNodes[i], aNodes[i+1] );
3194       // check if elements sharing a link can be fused
3195       itLE = mapLi_listEl.find( link );
3196       if ( itLE != mapLi_listEl.end() ) {
3197         if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
3198           continue;
3199         const SMDS_MeshElement* elem2 = (*itLE).second.front();
3200         //if ( FindShape( elem ) != FindShape( elem2 ))
3201         //  continue; // do not fuse triangles laying on different shapes
3202         if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
3203           continue; // avoid making badly shaped quads
3204         (*itLE).second.push_back( elem );
3205       }
3206       else {
3207         mapLi_listEl[ link ].push_back( elem );
3208       }
3209       mapEl_setLi [ elem ].insert( link );
3210     }
3211   }
3212   // Clean the maps from the links shared by a sole element, ie
3213   // links to which only one element is bound in mapLi_listEl
3214
3215   for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
3216     int nbElems = (*itLE).second.size();
3217     if ( nbElems < 2  ) {
3218       const SMDS_MeshElement* elem = (*itLE).second.front();
3219       SMESH_TLink link = (*itLE).first;
3220       mapEl_setLi[ elem ].erase( link );
3221       if ( mapEl_setLi[ elem ].empty() )
3222         mapEl_setLi.erase( elem );
3223     }
3224   }
3225
3226   // Algo: fuse triangles into quadrangles
3227
3228   while ( ! mapEl_setLi.empty() ) {
3229     // Look for the start element:
3230     // the element having the least nb of shared links
3231     const SMDS_MeshElement* startElem = 0;
3232     int minNbLinks = 4;
3233     for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
3234       int nbLinks = (*itEL).second.size();
3235       if ( nbLinks < minNbLinks ) {
3236         startElem = (*itEL).first;
3237         minNbLinks = nbLinks;
3238         if ( minNbLinks == 1 )
3239           break;
3240       }
3241     }
3242
3243     // search elements to fuse starting from startElem or links of elements
3244     // fused earlyer - startLinks
3245     list< SMESH_TLink > startLinks;
3246     while ( startElem || !startLinks.empty() ) {
3247       while ( !startElem && !startLinks.empty() ) {
3248         // Get an element to start, by a link
3249         SMESH_TLink linkId = startLinks.front();
3250         startLinks.pop_front();
3251         itLE = mapLi_listEl.find( linkId );
3252         if ( itLE != mapLi_listEl.end() ) {
3253           list< const SMDS_MeshElement* > & listElem = (*itLE).second;
3254           list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
3255           for ( ; itE != listElem.end() ; itE++ )
3256             if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
3257               startElem = (*itE);
3258           mapLi_listEl.erase( itLE );
3259         }
3260       }
3261
3262       if ( startElem ) {
3263         // Get candidates to be fused
3264         const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
3265         const SMESH_TLink *link12 = 0, *link13 = 0;
3266         startElem = 0;
3267         ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
3268         set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
3269         ASSERT( !setLi.empty() );
3270         set< SMESH_TLink >::iterator itLi;
3271         for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
3272         {
3273           const SMESH_TLink & link = (*itLi);
3274           itLE = mapLi_listEl.find( link );
3275           if ( itLE == mapLi_listEl.end() )
3276             continue;
3277
3278           const SMDS_MeshElement* elem = (*itLE).second.front();
3279           if ( elem == tr1 )
3280             elem = (*itLE).second.back();
3281           mapLi_listEl.erase( itLE );
3282           if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
3283             continue;
3284           if ( tr2 ) {
3285             tr3 = elem;
3286             link13 = &link;
3287           }
3288           else {
3289             tr2 = elem;
3290             link12 = &link;
3291           }
3292
3293           // add other links of elem to list of links to re-start from
3294           set< SMESH_TLink >& links = mapEl_setLi[ elem ];
3295           set< SMESH_TLink >::iterator it;
3296           for ( it = links.begin(); it != links.end(); it++ ) {
3297             const SMESH_TLink& link2 = (*it);
3298             if ( link2 != link )
3299               startLinks.push_back( link2 );
3300           }
3301         }
3302
3303         // Get nodes of possible quadrangles
3304         const SMDS_MeshNode *n12 [4], *n13 [4];
3305         bool Ok12 = false, Ok13 = false;
3306         const SMDS_MeshNode *linkNode1, *linkNode2;
3307         if(tr2) {
3308           linkNode1 = link12->first;
3309           linkNode2 = link12->second;
3310           if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
3311             Ok12 = true;
3312         }
3313         if(tr3) {
3314           linkNode1 = link13->first;
3315           linkNode2 = link13->second;
3316           if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
3317             Ok13 = true;
3318         }
3319
3320         // Choose a pair to fuse
3321         if ( Ok12 && Ok13 ) {
3322           SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
3323           SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
3324           double aBadRate12 = getBadRate( &quad12, theCrit );
3325           double aBadRate13 = getBadRate( &quad13, theCrit );
3326           if (  aBadRate13 < aBadRate12 )
3327             Ok12 = false;
3328           else
3329             Ok13 = false;
3330         }
3331
3332         // Make quadrangles
3333         // and remove fused elems and remove links from the maps
3334         mapEl_setLi.erase( tr1 );
3335         if ( Ok12 )
3336         {
3337           mapEl_setLi.erase( tr2 );
3338           mapLi_listEl.erase( *link12 );
3339           if ( tr1->NbNodes() == 3 )
3340           {
3341             const SMDS_MeshElement* newElem = 0;
3342             newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
3343             myLastCreatedElems.push_back(newElem);
3344             AddToSameGroups( newElem, tr1, aMesh );
3345             int aShapeId = tr1->getshapeId();
3346             if ( aShapeId )
3347               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3348             aMesh->RemoveElement( tr1 );
3349             aMesh->RemoveElement( tr2 );
3350           }
3351           else {
3352             vector< const SMDS_MeshNode* > N1;
3353             vector< const SMDS_MeshNode* > N2;
3354             getNodesFromTwoTria(tr1,tr2,N1,N2);
3355             // now we receive following N1 and N2 (using numeration as in image in InverseDiag())
3356             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
3357             // i.e. first nodes from both arrays form a new diagonal
3358             const SMDS_MeshNode* aNodes[8];
3359             aNodes[0] = N1[0];
3360             aNodes[1] = N1[1];
3361             aNodes[2] = N2[0];
3362             aNodes[3] = N2[1];
3363             aNodes[4] = N1[3];
3364             aNodes[5] = N2[5];
3365             aNodes[6] = N2[3];
3366             aNodes[7] = N1[5];
3367             const SMDS_MeshElement* newElem = 0;
3368             if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3369               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3370                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3371             else
3372               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3373                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3374             myLastCreatedElems.push_back(newElem);
3375             AddToSameGroups( newElem, tr1, aMesh );
3376             int aShapeId = tr1->getshapeId();
3377             if ( aShapeId )
3378               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3379             aMesh->RemoveElement( tr1 );
3380             aMesh->RemoveElement( tr2 );
3381             // remove middle node (9)
3382             if ( N1[4]->NbInverseElements() == 0 )
3383               aMesh->RemoveNode( N1[4] );
3384             if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3385               aMesh->RemoveNode( N1[6] );
3386             if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3387               aMesh->RemoveNode( N2[6] );
3388           }
3389         }
3390         else if ( Ok13 )
3391         {
3392           mapEl_setLi.erase( tr3 );
3393           mapLi_listEl.erase( *link13 );
3394           if ( tr1->NbNodes() == 3 ) {
3395             const SMDS_MeshElement* newElem = 0;
3396             newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
3397             myLastCreatedElems.push_back(newElem);
3398             AddToSameGroups( newElem, tr1, aMesh );
3399             int aShapeId = tr1->getshapeId();
3400             if ( aShapeId )
3401               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3402             aMesh->RemoveElement( tr1 );
3403             aMesh->RemoveElement( tr3 );
3404           }
3405           else {
3406             vector< const SMDS_MeshNode* > N1;
3407             vector< const SMDS_MeshNode* > N2;
3408             getNodesFromTwoTria(tr1,tr3,N1,N2);
3409             // now we receive following N1 and N2 (using numeration as above image)
3410             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
3411             // i.e. first nodes from both arrays form a new diagonal
3412             const SMDS_MeshNode* aNodes[8];
3413             aNodes[0] = N1[0];
3414             aNodes[1] = N1[1];
3415             aNodes[2] = N2[0];
3416             aNodes[3] = N2[1];
3417             aNodes[4] = N1[3];
3418             aNodes[5] = N2[5];
3419             aNodes[6] = N2[3];
3420             aNodes[7] = N1[5];
3421             const SMDS_MeshElement* newElem = 0;
3422             if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3423               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3424                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3425             else
3426               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3427                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3428             myLastCreatedElems.push_back(newElem);
3429             AddToSameGroups( newElem, tr1, aMesh );
3430             int aShapeId = tr1->getshapeId();
3431             if ( aShapeId )
3432               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3433             aMesh->RemoveElement( tr1 );
3434             aMesh->RemoveElement( tr3 );
3435             // remove middle node (9)
3436             if ( N1[4]->NbInverseElements() == 0 )
3437               aMesh->RemoveNode( N1[4] );
3438             if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3439               aMesh->RemoveNode( N1[6] );
3440             if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3441               aMesh->RemoveNode( N2[6] );
3442           }
3443         }
3444
3445         // Next element to fuse: the rejected one
3446         if ( tr3 )
3447           startElem = Ok12 ? tr3 : tr2;
3448
3449       } // if ( startElem )
3450     } // while ( startElem || !startLinks.empty() )
3451   } // while ( ! mapEl_setLi.empty() )
3452
3453   return true;
3454 }
3455
3456 //================================================================================
3457 /*!
3458  * \brief Return nodes linked to the given one
3459  * \param theNode - the node
3460  * \param linkedNodes - the found nodes
3461  * \param type - the type of elements to check
3462  *
3463  * Medium nodes are ignored
3464  */
3465 //================================================================================
3466
3467 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
3468                                        TIDSortedElemSet &   linkedNodes,
3469                                        SMDSAbs_ElementType  type )
3470 {
3471   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
3472   while ( elemIt->more() )
3473   {
3474     const SMDS_MeshElement* elem = elemIt->next();
3475     if(elem->GetType() == SMDSAbs_0DElement)
3476       continue;
3477
3478     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
3479     if ( elem->GetType() == SMDSAbs_Volume )
3480     {
3481       SMDS_VolumeTool vol( elem );
3482       while ( nodeIt->more() ) {
3483         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3484         if ( theNode != n && vol.IsLinked( theNode, n ))
3485           linkedNodes.insert( n );
3486       }
3487     }
3488     else
3489     {
3490       for ( int i = 0; nodeIt->more(); ++i ) {
3491         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3492         if ( n == theNode ) {
3493           int iBefore = i - 1;
3494           int iAfter  = i + 1;
3495           if ( elem->IsQuadratic() ) {
3496             int nb = elem->NbNodes() / 2;
3497             iAfter  = SMESH_MesherHelper::WrapIndex( iAfter, nb );
3498             iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
3499           }
3500           linkedNodes.insert( elem->GetNodeWrap( iAfter ));
3501           linkedNodes.insert( elem->GetNodeWrap( iBefore ));
3502         }
3503       }
3504     }
3505   }
3506 }
3507
3508 //=======================================================================
3509 //function : laplacianSmooth
3510 //purpose  : pulls theNode toward the center of surrounding nodes directly
3511 //           connected to that node along an element edge
3512 //=======================================================================
3513
3514 void laplacianSmooth(const SMDS_MeshNode*                 theNode,
3515                      const Handle(Geom_Surface)&          theSurface,
3516                      map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3517 {
3518   // find surrounding nodes
3519
3520   TIDSortedElemSet nodeSet;
3521   SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
3522
3523   // compute new coodrs
3524
3525   double coord[] = { 0., 0., 0. };
3526   TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3527   for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
3528     const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3529     if ( theSurface.IsNull() ) { // smooth in 3D
3530       coord[0] += node->X();
3531       coord[1] += node->Y();
3532       coord[2] += node->Z();
3533     }
3534     else { // smooth in 2D
3535       ASSERT( theUVMap.find( node ) != theUVMap.end() );
3536       gp_XY* uv = theUVMap[ node ];
3537       coord[0] += uv->X();
3538       coord[1] += uv->Y();
3539     }
3540   }
3541   int nbNodes = nodeSet.size();
3542   if ( !nbNodes )
3543     return;
3544   coord[0] /= nbNodes;
3545   coord[1] /= nbNodes;
3546
3547   if ( !theSurface.IsNull() ) {
3548     ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3549     theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3550     gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3551     coord[0] = p3d.X();
3552     coord[1] = p3d.Y();
3553     coord[2] = p3d.Z();
3554   }
3555   else
3556     coord[2] /= nbNodes;
3557
3558   // move node
3559
3560   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3561 }
3562
3563 //=======================================================================
3564 //function : centroidalSmooth
3565 //purpose  : pulls theNode toward the element-area-weighted centroid of the
3566 //           surrounding elements
3567 //=======================================================================
3568
3569 void centroidalSmooth(const SMDS_MeshNode*                 theNode,
3570                       const Handle(Geom_Surface)&          theSurface,
3571                       map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3572 {
3573   gp_XYZ aNewXYZ(0.,0.,0.);
3574   SMESH::Controls::Area anAreaFunc;
3575   double totalArea = 0.;
3576   int nbElems = 0;
3577
3578   // compute new XYZ
3579
3580   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3581   while ( elemIt->more() )
3582   {
3583     const SMDS_MeshElement* elem = elemIt->next();
3584     nbElems++;
3585
3586     gp_XYZ elemCenter(0.,0.,0.);
3587     SMESH::Controls::TSequenceOfXYZ aNodePoints;
3588     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3589     int nn = elem->NbNodes();
3590     if(elem->IsQuadratic()) nn = nn/2;
3591     int i=0;
3592     //while ( itN->more() ) {
3593     while ( i<nn ) {
3594       const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3595       i++;
3596       gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3597       aNodePoints.push_back( aP );
3598       if ( !theSurface.IsNull() ) { // smooth in 2D
3599         ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3600         gp_XY* uv = theUVMap[ aNode ];
3601         aP.SetCoord( uv->X(), uv->Y(), 0. );
3602       }
3603       elemCenter += aP;
3604     }
3605     double elemArea = anAreaFunc.GetValue( aNodePoints );
3606     totalArea += elemArea;
3607     elemCenter /= nn;
3608     aNewXYZ += elemCenter * elemArea;
3609   }
3610   aNewXYZ /= totalArea;
3611   if ( !theSurface.IsNull() ) {
3612     theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3613     aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3614   }
3615
3616   // move node
3617
3618   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3619 }
3620
3621 //=======================================================================
3622 //function : getClosestUV
3623 //purpose  : return UV of closest projection
3624 //=======================================================================
3625
3626 static bool getClosestUV (Extrema_GenExtPS& projector,
3627                           const gp_Pnt&     point,
3628                           gp_XY &           result)
3629 {
3630   projector.Perform( point );
3631   if ( projector.IsDone() ) {
3632     double u, v, minVal = DBL_MAX;
3633     for ( int i = projector.NbExt(); i > 0; i-- )
3634       if ( projector.SquareDistance( i ) < minVal ) {
3635         minVal = projector.SquareDistance( i );
3636         projector.Point( i ).Parameter( u, v );
3637       }
3638     result.SetCoord( u, v );
3639     return true;
3640   }
3641   return false;
3642 }
3643
3644 //=======================================================================
3645 //function : Smooth
3646 //purpose  : Smooth theElements during theNbIterations or until a worst
3647 //           element has aspect ratio <= theTgtAspectRatio.
3648 //           Aspect Ratio varies in range [1.0, inf].
3649 //           If theElements is empty, the whole mesh is smoothed.
3650 //           theFixedNodes contains additionally fixed nodes. Nodes built
3651 //           on edges and boundary nodes are always fixed.
3652 //=======================================================================
3653
3654 void SMESH_MeshEditor::Smooth (TIDSortedElemSet &          theElems,
3655                                set<const SMDS_MeshNode*> & theFixedNodes,
3656                                const SmoothMethod          theSmoothMethod,
3657                                const int                   theNbIterations,
3658                                double                      theTgtAspectRatio,
3659                                const bool                  the2D)
3660 {
3661   ClearLastCreated();
3662
3663   if ( theTgtAspectRatio < 1.0 )
3664     theTgtAspectRatio = 1.0;
3665
3666   const double disttol = 1.e-16;
3667
3668   SMESH::Controls::AspectRatio aQualityFunc;
3669
3670   SMESHDS_Mesh* aMesh = GetMeshDS();
3671
3672   if ( theElems.empty() ) {
3673     // add all faces to theElems
3674     SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3675     while ( fIt->more() ) {
3676       const SMDS_MeshElement* face = fIt->next();
3677       theElems.insert( theElems.end(), face );
3678     }
3679   }
3680   // get all face ids theElems are on
3681   set< int > faceIdSet;
3682   TIDSortedElemSet::iterator itElem;
3683   if ( the2D )
3684     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3685       int fId = FindShape( *itElem );
3686       // check that corresponding submesh exists and a shape is face
3687       if (fId &&
3688           faceIdSet.find( fId ) == faceIdSet.end() &&
3689           aMesh->MeshElements( fId )) {
3690         TopoDS_Shape F = aMesh->IndexToShape( fId );
3691         if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3692           faceIdSet.insert( fId );
3693       }
3694     }
3695   faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3696
3697   // ===============================================
3698   // smooth elements on each TopoDS_Face separately
3699   // ===============================================
3700
3701   SMESH_MesherHelper helper( *GetMesh() );
3702
3703   set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treat 0 fId at the end
3704   for ( ; fId != faceIdSet.rend(); ++fId )
3705   {
3706     // get face surface and submesh
3707     Handle(Geom_Surface) surface;
3708     SMESHDS_SubMesh* faceSubMesh = 0;
3709     TopoDS_Face face;
3710     double fToler2 = 0;
3711     double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3712     bool isUPeriodic = false, isVPeriodic = false;
3713     if ( *fId )
3714     {
3715       face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3716       surface = BRep_Tool::Surface( face );
3717       faceSubMesh = aMesh->MeshElements( *fId );
3718       fToler2 = BRep_Tool::Tolerance( face );
3719       fToler2 *= fToler2 * 10.;
3720       isUPeriodic = surface->IsUPeriodic();
3721       // if ( isUPeriodic )
3722       //   surface->UPeriod();
3723       isVPeriodic = surface->IsVPeriodic();
3724       // if ( isVPeriodic )
3725       //   surface->VPeriod();
3726       surface->Bounds( u1, u2, v1, v2 );
3727       helper.SetSubShape( face );
3728     }
3729     // ---------------------------------------------------------
3730     // for elements on a face, find movable and fixed nodes and
3731     // compute UV for them
3732     // ---------------------------------------------------------
3733     bool checkBoundaryNodes = false;
3734     bool isQuadratic = false;
3735     set<const SMDS_MeshNode*> setMovableNodes;
3736     map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
3737     list< gp_XY > listUV; // uvs the 2 uvMaps refer to
3738     list< const SMDS_MeshElement* > elemsOnFace;
3739
3740     Extrema_GenExtPS projector;
3741     GeomAdaptor_Surface surfAdaptor;
3742     if ( !surface.IsNull() ) {
3743       surfAdaptor.Load( surface );
3744       projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
3745     }
3746     int nbElemOnFace = 0;
3747     itElem = theElems.begin();
3748     // loop on not yet smoothed elements: look for elems on a face
3749     while ( itElem != theElems.end() )
3750     {
3751       if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
3752         break; // all elements found
3753
3754       const SMDS_MeshElement* elem = *itElem;
3755       if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
3756            ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
3757         ++itElem;
3758         continue;
3759       }
3760       elemsOnFace.push_back( elem );
3761       theElems.erase( itElem++ );
3762       nbElemOnFace++;
3763
3764       if ( !isQuadratic )
3765         isQuadratic = elem->IsQuadratic();
3766
3767       // get movable nodes of elem
3768       const SMDS_MeshNode* node;
3769       SMDS_TypeOfPosition posType;
3770       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3771       int nn = 0, nbn =  elem->NbNodes();
3772       if(elem->IsQuadratic())
3773         nbn = nbn/2;
3774       while ( nn++ < nbn ) {
3775         node = static_cast<const SMDS_MeshNode*>( itN->next() );
3776         const SMDS_PositionPtr& pos = node->GetPosition();
3777         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3778         if (posType != SMDS_TOP_EDGE &&
3779             posType != SMDS_TOP_VERTEX &&
3780             theFixedNodes.find( node ) == theFixedNodes.end())
3781         {
3782           // check if all faces around the node are on faceSubMesh
3783           // because a node on edge may be bound to face
3784           bool all = true;
3785           if ( faceSubMesh ) {
3786             SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3787             while ( eIt->more() && all ) {
3788               const SMDS_MeshElement* e = eIt->next();
3789               all = faceSubMesh->Contains( e );
3790             }
3791           }
3792           if ( all )
3793             setMovableNodes.insert( node );
3794           else
3795             checkBoundaryNodes = true;
3796         }
3797         if ( posType == SMDS_TOP_3DSPACE )
3798           checkBoundaryNodes = true;
3799       }
3800
3801       if ( surface.IsNull() )
3802         continue;
3803
3804       // get nodes to check UV
3805       list< const SMDS_MeshNode* > uvCheckNodes;
3806       const SMDS_MeshNode* nodeInFace = 0;
3807       itN = elem->nodesIterator();
3808       nn = 0; nbn =  elem->NbNodes();
3809       if(elem->IsQuadratic())
3810         nbn = nbn/2;
3811       while ( nn++ < nbn ) {
3812         node = static_cast<const SMDS_MeshNode*>( itN->next() );
3813         if ( node->GetPosition()->GetDim() == 2 )
3814           nodeInFace = node;
3815         if ( uvMap.find( node ) == uvMap.end() )
3816           uvCheckNodes.push_back( node );
3817         // add nodes of elems sharing node
3818         //         SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3819         //         while ( eIt->more() ) {
3820         //           const SMDS_MeshElement* e = eIt->next();
3821         //           if ( e != elem ) {
3822         //             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3823         //             while ( nIt->more() ) {
3824         //               const SMDS_MeshNode* n =
3825         //                 static_cast<const SMDS_MeshNode*>( nIt->next() );
3826         //               if ( uvMap.find( n ) == uvMap.end() )
3827         //                 uvCheckNodes.push_back( n );
3828         //             }
3829         //           }
3830         //         }
3831       }
3832       // check UV on face
3833       list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
3834       for ( ; n != uvCheckNodes.end(); ++n ) {
3835         node = *n;
3836         gp_XY uv( 0, 0 );
3837         const SMDS_PositionPtr& pos = node->GetPosition();
3838         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3839         // get existing UV
3840         if ( pos )
3841         {
3842           bool toCheck = true;
3843           uv = helper.GetNodeUV( face, node, nodeInFace, &toCheck );
3844         }
3845         // compute not existing UV
3846         bool project = ( posType == SMDS_TOP_3DSPACE );
3847         // double dist1 = DBL_MAX, dist2 = 0;
3848         // if ( posType != SMDS_TOP_3DSPACE ) {
3849         //   dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
3850         //   project = dist1 > fToler2;
3851         // }
3852         if ( project ) { // compute new UV
3853           gp_XY newUV;
3854           gp_Pnt pNode = SMESH_NodeXYZ( node );
3855           if ( !getClosestUV( projector, pNode, newUV )) {
3856             MESSAGE("Node Projection Failed " << node);
3857           }
3858           else {
3859             if ( isUPeriodic )
3860               newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
3861             if ( isVPeriodic )
3862               newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
3863             // check new UV
3864             // if ( posType != SMDS_TOP_3DSPACE )
3865             //   dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
3866             // if ( dist2 < dist1 )
3867             uv = newUV;
3868           }
3869         }
3870         // store UV in the map
3871         listUV.push_back( uv );
3872         uvMap.insert( make_pair( node, &listUV.back() ));
3873       }
3874     } // loop on not yet smoothed elements
3875
3876     if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
3877       checkBoundaryNodes = true;
3878
3879     // fix nodes on mesh boundary
3880
3881     if ( checkBoundaryNodes ) {
3882       map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
3883       map< SMESH_TLink, int >::iterator link_nb;
3884       // put all elements links to linkNbMap
3885       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3886       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3887         const SMDS_MeshElement* elem = (*elemIt);
3888         int nbn =  elem->NbCornerNodes();
3889         // loop on elem links: insert them in linkNbMap
3890         for ( int iN = 0; iN < nbn; ++iN ) {
3891           const SMDS_MeshNode* n1 = elem->GetNode( iN );
3892           const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
3893           SMESH_TLink link( n1, n2 );
3894           link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
3895           link_nb->second++;
3896         }
3897       }
3898       // remove nodes that are in links encountered only once from setMovableNodes
3899       for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
3900         if ( link_nb->second == 1 ) {
3901           setMovableNodes.erase( link_nb->first.node1() );
3902           setMovableNodes.erase( link_nb->first.node2() );
3903         }
3904       }
3905     }
3906
3907     // -----------------------------------------------------
3908     // for nodes on seam edge, compute one more UV ( uvMap2 );
3909     // find movable nodes linked to nodes on seam and which
3910     // are to be smoothed using the second UV ( uvMap2 )
3911     // -----------------------------------------------------
3912
3913     set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
3914     if ( !surface.IsNull() ) {
3915       TopExp_Explorer eExp( face, TopAbs_EDGE );
3916       for ( ; eExp.More(); eExp.Next() ) {
3917         TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
3918         if ( !BRep_Tool::IsClosed( edge, face ))
3919           continue;
3920         SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
3921         if ( !sm ) continue;
3922         // find out which parameter varies for a node on seam
3923         double f,l;
3924         gp_Pnt2d uv1, uv2;
3925         Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3926         if ( pcurve.IsNull() ) continue;
3927         uv1 = pcurve->Value( f );
3928         edge.Reverse();
3929         pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3930         if ( pcurve.IsNull() ) continue;
3931         uv2 = pcurve->Value( f );
3932         int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
3933         // assure uv1 < uv2
3934         if ( uv1.Coord( iPar ) > uv2.Coord( iPar ))
3935           std::swap( uv1, uv2 );
3936         // get nodes on seam and its vertices
3937         list< const SMDS_MeshNode* > seamNodes;
3938         SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
3939         while ( nSeamIt->more() ) {
3940           const SMDS_MeshNode* node = nSeamIt->next();
3941           if ( !isQuadratic || !IsMedium( node ))
3942             seamNodes.push_back( node );
3943         }
3944         TopExp_Explorer vExp( edge, TopAbs_VERTEX );
3945         for ( ; vExp.More(); vExp.Next() ) {
3946           sm = aMesh->MeshElements( vExp.Current() );
3947           if ( sm ) {
3948             nSeamIt = sm->GetNodes();
3949             while ( nSeamIt->more() )
3950               seamNodes.push_back( nSeamIt->next() );
3951           }
3952         }
3953         // loop on nodes on seam
3954         list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
3955         for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
3956           const SMDS_MeshNode* nSeam = *noSeIt;
3957           map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
3958           if ( n_uv == uvMap.end() )
3959             continue;
3960           // set the first UV
3961           n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
3962           // set the second UV
3963           listUV.push_back( *n_uv->second );
3964           listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
3965           if ( uvMap2.empty() )
3966             uvMap2 = uvMap; // copy the uvMap contents
3967           uvMap2[ nSeam ] = &listUV.back();
3968
3969           // collect movable nodes linked to ones on seam in nodesNearSeam
3970           SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
3971           while ( eIt->more() ) {
3972             const SMDS_MeshElement* e = eIt->next();
3973             int nbUseMap1 = 0, nbUseMap2 = 0;
3974             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3975             int nn = 0, nbn =  e->NbNodes();
3976             if(e->IsQuadratic()) nbn = nbn/2;
3977             while ( nn++ < nbn )
3978             {
3979               const SMDS_MeshNode* n =
3980                 static_cast<const SMDS_MeshNode*>( nIt->next() );
3981               if (n == nSeam ||
3982                   setMovableNodes.find( n ) == setMovableNodes.end() )
3983                 continue;
3984               // add only nodes being closer to uv2 than to uv1
3985               // gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
3986               //              0.5 * ( n->Y() + nSeam->Y() ),
3987               //              0.5 * ( n->Z() + nSeam->Z() ));
3988               // gp_XY uv;
3989               // getClosestUV( projector, pMid, uv );
3990               double x = uvMap[ n ]->Coord( iPar );
3991               if ( Abs( uv1.Coord( iPar ) - x ) >
3992                    Abs( uv2.Coord( iPar ) - x )) {
3993                 nodesNearSeam.insert( n );
3994                 nbUseMap2++;
3995               }
3996               else
3997                 nbUseMap1++;
3998             }
3999             // for centroidalSmooth all element nodes must
4000             // be on one side of a seam
4001             if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
4002               SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4003               nn = 0;
4004               while ( nn++ < nbn ) {
4005                 const SMDS_MeshNode* n =
4006                   static_cast<const SMDS_MeshNode*>( nIt->next() );
4007                 setMovableNodes.erase( n );
4008               }
4009             }
4010           }
4011         } // loop on nodes on seam
4012       } // loop on edge of a face
4013     } // if ( !face.IsNull() )
4014
4015     if ( setMovableNodes.empty() ) {
4016       MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
4017       continue; // goto next face
4018     }
4019
4020     // -------------
4021     // SMOOTHING //
4022     // -------------
4023
4024     int it = -1;
4025     double maxRatio = -1., maxDisplacement = -1.;
4026     set<const SMDS_MeshNode*>::iterator nodeToMove;
4027     for ( it = 0; it < theNbIterations; it++ ) {
4028       maxDisplacement = 0.;
4029       nodeToMove = setMovableNodes.begin();
4030       for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4031         const SMDS_MeshNode* node = (*nodeToMove);
4032         gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
4033
4034         // smooth
4035         bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
4036         if ( theSmoothMethod == LAPLACIAN )
4037           laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
4038         else
4039           centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
4040
4041         // node displacement
4042         gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
4043         Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
4044         if ( aDispl > maxDisplacement )
4045           maxDisplacement = aDispl;
4046       }
4047       // no node movement => exit
4048       //if ( maxDisplacement < 1.e-16 ) {
4049       if ( maxDisplacement < disttol ) {
4050         MESSAGE("-- no node movement --");
4051         break;
4052       }
4053
4054       // check elements quality
4055       maxRatio  = 0;
4056       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4057       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4058         const SMDS_MeshElement* elem = (*elemIt);
4059         if ( !elem || elem->GetType() != SMDSAbs_Face )
4060           continue;
4061         SMESH::Controls::TSequenceOfXYZ aPoints;
4062         if ( aQualityFunc.GetPoints( elem, aPoints )) {
4063           double aValue = aQualityFunc.GetValue( aPoints );
4064           if ( aValue > maxRatio )
4065             maxRatio = aValue;
4066         }
4067       }
4068       if ( maxRatio <= theTgtAspectRatio ) {
4069         //MESSAGE("-- quality achieved --");
4070         break;
4071       }
4072       if (it+1 == theNbIterations) {
4073         //MESSAGE("-- Iteration limit exceeded --");
4074       }
4075     } // smoothing iterations
4076
4077     // MESSAGE(" Face id: " << *fId <<
4078     //         " Nb iterstions: " << it <<
4079     //         " Displacement: " << maxDisplacement <<
4080     //         " Aspect Ratio " << maxRatio);
4081
4082     // ---------------------------------------
4083     // new nodes positions are computed,
4084     // record movement in DS and set new UV
4085     // ---------------------------------------
4086     nodeToMove = setMovableNodes.begin();
4087     for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4088       SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
4089       aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
4090       map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
4091       if ( node_uv != uvMap.end() ) {
4092         gp_XY* uv = node_uv->second;
4093         node->SetPosition
4094           ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
4095       }
4096     }
4097
4098     // move medium nodes of quadratic elements
4099     if ( isQuadratic )
4100     {
4101       vector<const SMDS_MeshNode*> nodes;
4102       bool checkUV;
4103       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4104       for ( ; elemIt != elemsOnFace.end(); ++elemIt )
4105       {
4106         const SMDS_MeshElement* QF = *elemIt;
4107         if ( QF->IsQuadratic() )
4108         {
4109           nodes.assign( SMDS_MeshElement::iterator( QF->interlacedNodesIterator() ),
4110                         SMDS_MeshElement::iterator() );
4111           nodes.push_back( nodes[0] );
4112           gp_Pnt xyz;
4113           for (size_t i = 1; i < nodes.size(); i += 2 ) // i points to a medium node
4114           {
4115             if ( !surface.IsNull() )
4116             {
4117               gp_XY uv1 = helper.GetNodeUV( face, nodes[i-1], nodes[i+1], &checkUV );
4118               gp_XY uv2 = helper.GetNodeUV( face, nodes[i+1], nodes[i-1], &checkUV );
4119               gp_XY uv  = helper.GetMiddleUV( surface, uv1, uv2 );
4120               xyz = surface->Value( uv.X(), uv.Y() );
4121             }
4122             else {
4123               xyz = 0.5 * ( SMESH_NodeXYZ( nodes[i-1] ) + SMESH_NodeXYZ( nodes[i+1] ));
4124             }
4125             if (( SMESH_NodeXYZ( nodes[i] ) - xyz.XYZ() ).Modulus() > disttol )
4126               // we have to move a medium node
4127               aMesh->MoveNode( nodes[i], xyz.X(), xyz.Y(), xyz.Z() );
4128           }
4129         }
4130       }
4131     }
4132
4133   } // loop on face ids
4134
4135 }
4136
4137 namespace
4138 {
4139   //=======================================================================
4140   //function : isReverse
4141   //purpose  : Return true if normal of prevNodes is not co-directied with
4142   //           gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
4143   //           iNotSame is where prevNodes and nextNodes are different.
4144   //           If result is true then future volume orientation is OK
4145   //=======================================================================
4146
4147   bool isReverse(const SMDS_MeshElement*             face,
4148                  const vector<const SMDS_MeshNode*>& prevNodes,
4149                  const vector<const SMDS_MeshNode*>& nextNodes,
4150                  const int                           iNotSame)
4151   {
4152
4153     SMESH_NodeXYZ pP = prevNodes[ iNotSame ];
4154     SMESH_NodeXYZ pN = nextNodes[ iNotSame ];
4155     gp_XYZ extrDir( pN - pP ), faceNorm;
4156     SMESH_MeshAlgos::FaceNormal( face, faceNorm, /*normalized=*/false );
4157
4158     return faceNorm * extrDir < 0.0;
4159   }
4160
4161   //================================================================================
4162   /*!
4163    * \brief Assure that theElemSets[0] holds elements, not nodes
4164    */
4165   //================================================================================
4166
4167   void setElemsFirst( TIDSortedElemSet theElemSets[2] )
4168   {
4169     if ( !theElemSets[0].empty() &&
4170          (*theElemSets[0].begin())->GetType() == SMDSAbs_Node )
4171     {
4172       std::swap( theElemSets[0], theElemSets[1] );
4173     }
4174     else if ( !theElemSets[1].empty() &&
4175               (*theElemSets[1].begin())->GetType() != SMDSAbs_Node )
4176     {
4177       std::swap( theElemSets[0], theElemSets[1] );
4178     }
4179   }
4180 }
4181
4182 //=======================================================================
4183 /*!
4184  * \brief Create elements by sweeping an element
4185  * \param elem - element to sweep
4186  * \param newNodesItVec - nodes generated from each node of the element
4187  * \param newElems - generated elements
4188  * \param nbSteps - number of sweeping steps
4189  * \param srcElements - to append elem for each generated element
4190  */
4191 //=======================================================================
4192
4193 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement*               elem,
4194                                     const vector<TNodeOfNodeListMapItr> & newNodesItVec,
4195                                     list<const SMDS_MeshElement*>&        newElems,
4196                                     const size_t                          nbSteps,
4197                                     SMESH_SequenceOfElemPtr&              srcElements)
4198 {
4199   SMESHDS_Mesh* aMesh = GetMeshDS();
4200
4201   const int           nbNodes = elem->NbNodes();
4202   const int         nbCorners = elem->NbCornerNodes();
4203   SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
4204                                                           polyhedron creation !!! */
4205   // Loop on elem nodes:
4206   // find new nodes and detect same nodes indices
4207   vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
4208   vector<const SMDS_MeshNode*> prevNod( nbNodes );
4209   vector<const SMDS_MeshNode*> nextNod( nbNodes );
4210   vector<const SMDS_MeshNode*> midlNod( nbNodes );
4211
4212   int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
4213   vector<int> sames(nbNodes);
4214   vector<bool> isSingleNode(nbNodes);
4215
4216   for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4217     TNodeOfNodeListMapItr                        nnIt = newNodesItVec[ iNode ];
4218     const SMDS_MeshNode*                         node = nnIt->first;
4219     const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
4220     if ( listNewNodes.empty() )
4221       return;
4222
4223     itNN   [ iNode ] = listNewNodes.begin();
4224     prevNod[ iNode ] = node;
4225     nextNod[ iNode ] = listNewNodes.front();
4226
4227     isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
4228                                                              corner node of linear */
4229     if ( prevNod[ iNode ] != nextNod [ iNode ])
4230       nbDouble += !isSingleNode[iNode];
4231
4232     if( iNode < nbCorners ) { // check corners only
4233       if ( prevNod[ iNode ] == nextNod [ iNode ])
4234         sames[nbSame++] = iNode;
4235       else
4236         iNotSameNode = iNode;
4237     }
4238   }
4239
4240   if ( nbSame == nbNodes || nbSame > 2) {
4241     MESSAGE( " Too many same nodes of element " << elem->GetID() );
4242     return;
4243   }
4244
4245   if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
4246   {
4247     // fix nodes order to have bottom normal external
4248     if ( baseType == SMDSEntity_Polygon )
4249     {
4250       std::reverse( itNN.begin(), itNN.end() );
4251       std::reverse( prevNod.begin(), prevNod.end() );
4252       std::reverse( midlNod.begin(), midlNod.end() );
4253       std::reverse( nextNod.begin(), nextNod.end() );
4254       std::reverse( isSingleNode.begin(), isSingleNode.end() );
4255     }
4256     else
4257     {
4258       const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType, nbNodes );
4259       SMDS_MeshCell::applyInterlace( ind, itNN );
4260       SMDS_MeshCell::applyInterlace( ind, prevNod );
4261       SMDS_MeshCell::applyInterlace( ind, nextNod );
4262       SMDS_MeshCell::applyInterlace( ind, midlNod );
4263       SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4264       if ( nbSame > 0 )
4265       {
4266         sames[nbSame] = iNotSameNode;
4267         for ( int j = 0; j <= nbSame; ++j )
4268           for ( size_t i = 0; i < ind.size(); ++i )
4269             if ( ind[i] == sames[j] )
4270             {
4271               sames[j] = i;
4272               break;
4273             }
4274         iNotSameNode = sames[nbSame];
4275       }
4276     }
4277   }
4278   else if ( elem->GetType() == SMDSAbs_Edge )
4279   {
4280     // orient a new face same as adjacent one
4281     int i1, i2;
4282     const SMDS_MeshElement* e;
4283     TIDSortedElemSet dummy;
4284     if (( e = SMESH_MeshAlgos::FindFaceInSet( nextNod[0], prevNod[0], dummy,dummy, &i1, &i2 )) ||
4285         ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[1], nextNod[1], dummy,dummy, &i1, &i2 )) ||
4286         ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[0], prevNod[1], dummy,dummy, &i1, &i2 )))
4287     {
4288       // there is an adjacent face, check order of nodes in it
4289       bool sameOrder = ( Abs( i2 - i1 ) == 1 ) ? ( i2 > i1 ) : ( i2 < i1 );
4290       if ( sameOrder )
4291       {
4292         std::swap( itNN[0],    itNN[1] );
4293         std::swap( prevNod[0], prevNod[1] );
4294         std::swap( nextNod[0], nextNod[1] );
4295         std::swap( isSingleNode[0], isSingleNode[1] );
4296         if ( nbSame > 0 )
4297           sames[0] = 1 - sames[0];
4298         iNotSameNode = 1 - iNotSameNode;
4299       }
4300     }
4301   }
4302
4303   int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
4304   if ( nbSame > 0 ) {
4305     iSameNode    = sames[ nbSame-1 ];
4306     iBeforeSame  = ( iSameNode + nbCorners - 1 ) % nbCorners;
4307     iAfterSame   = ( iSameNode + 1 ) % nbCorners;
4308     iOpposSame   = ( iSameNode - 2 < 0  ? iSameNode + 2 : iSameNode - 2 );
4309   }
4310
4311   if ( baseType == SMDSEntity_Polygon )
4312   {
4313     if      ( nbNodes == 3 ) baseType = SMDSEntity_Triangle;
4314     else if ( nbNodes == 4 ) baseType = SMDSEntity_Quadrangle;
4315   }
4316   else if ( baseType == SMDSEntity_Quad_Polygon )
4317   {
4318     if      ( nbNodes == 6 ) baseType = SMDSEntity_Quad_Triangle;
4319     else if ( nbNodes == 8 ) baseType = SMDSEntity_Quad_Quadrangle;
4320   }
4321
4322   // make new elements
4323   for ( size_t iStep = 0; iStep < nbSteps; iStep++ )
4324   {
4325     // get next nodes
4326     for ( iNode = 0; iNode < nbNodes; iNode++ )
4327     {
4328       midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
4329       nextNod[ iNode ] = *itNN[ iNode ]++;
4330     }
4331
4332     SMDS_MeshElement* aNewElem = 0;
4333     /*if(!elem->IsPoly())*/ {
4334       switch ( baseType ) {
4335       case SMDSEntity_0D:
4336       case SMDSEntity_Node: { // sweep NODE
4337         if ( nbSame == 0 ) {
4338           if ( isSingleNode[0] )
4339             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
4340           else
4341             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
4342         }
4343         else
4344           return;
4345         break;
4346       }
4347       case SMDSEntity_Edge: { // sweep EDGE
4348         if ( nbDouble == 0 )
4349         {
4350           if ( nbSame == 0 ) // ---> quadrangle
4351             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4352                                       nextNod[ 1 ], nextNod[ 0 ] );
4353           else               // ---> triangle
4354             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4355                                       nextNod[ iNotSameNode ] );
4356         }
4357         else                 // ---> polygon
4358         {
4359           vector<const SMDS_MeshNode*> poly_nodes;
4360           poly_nodes.push_back( prevNod[0] );
4361           poly_nodes.push_back( prevNod[1] );
4362           if ( prevNod[1] != nextNod[1] )
4363           {
4364             if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
4365             poly_nodes.push_back( nextNod[1] );
4366           }
4367           if ( prevNod[0] != nextNod[0] )
4368           {
4369             poly_nodes.push_back( nextNod[0] );
4370             if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
4371           }
4372           switch ( poly_nodes.size() ) {
4373           case 3:
4374             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
4375             break;
4376           case 4:
4377             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
4378                                        poly_nodes[ 2 ], poly_nodes[ 3 ]);
4379             break;
4380           default:
4381             aNewElem = aMesh->AddPolygonalFace (poly_nodes);
4382           }
4383         }
4384         break;
4385       }
4386       case SMDSEntity_Triangle: // TRIANGLE --->
4387       {
4388         if ( nbDouble > 0 ) break;
4389         if ( nbSame == 0 )       // ---> pentahedron
4390           aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4391                                        nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
4392
4393         else if ( nbSame == 1 )  // ---> pyramid
4394           aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4395                                        nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
4396                                        nextNod[ iSameNode ]);
4397
4398         else // 2 same nodes:       ---> tetrahedron
4399           aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4400                                        nextNod[ iNotSameNode ]);
4401         break;
4402       }
4403       case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
4404       {
4405         if ( nbSame == 2 )
4406           return;
4407         if ( nbDouble+nbSame == 2 )
4408         {
4409           if(nbSame==0) {      // ---> quadratic quadrangle
4410             aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4411                                       prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
4412           }
4413           else { //(nbSame==1) // ---> quadratic triangle
4414             if(sames[0]==2) {
4415               return; // medium node on axis
4416             }
4417             else if(sames[0]==0)
4418               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1],
4419                                         prevNod[2], midlNod[1], nextNod[2] );
4420             else // sames[0]==1
4421               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[0],
4422                                         prevNod[2], nextNod[2], midlNod[0]);
4423           }
4424         }
4425         else if ( nbDouble == 3 )
4426         {
4427           if ( nbSame == 0 ) {  // ---> bi-quadratic quadrangle
4428             aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4429                                       prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
4430           }
4431         }
4432         else
4433           return;
4434         break;
4435       }
4436       case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
4437         if ( nbDouble > 0 ) break;
4438
4439         if ( nbSame == 0 )       // ---> hexahedron
4440           aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
4441                                        nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
4442
4443         else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
4444           aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4445                                        nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
4446                                        nextNod[ iSameNode ]);
4447           newElems.push_back( aNewElem );
4448           aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ],  prevNod[ iOpposSame ],
4449                                        prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
4450                                        nextNod[ iOpposSame ],  nextNod[ iBeforeSame ] );
4451         }
4452         else if ( nbSame == 2 ) { // ---> pentahedron
4453           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
4454             // iBeforeSame is same too
4455             aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
4456                                          nextNod[ iOpposSame ],  prevNod[ iSameNode ],
4457                                          prevNod[ iAfterSame ],  nextNod[ iAfterSame ]);
4458           else
4459             // iAfterSame is same too
4460             aNewElem = aMesh->AddVolume (prevNod[ iSameNode ],   prevNod[ iBeforeSame ],
4461                                          nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
4462                                          prevNod[ iOpposSame ],  nextNod[ iOpposSame ]);
4463         }
4464         break;
4465       }
4466       case SMDSEntity_Quad_Triangle:  // sweep (Bi)Quadratic TRIANGLE --->
4467       case SMDSEntity_BiQuad_Triangle: /* ??? */ {
4468         if ( nbDouble+nbSame != 3 ) break;
4469         if(nbSame==0) {
4470           // --->  pentahedron with 15 nodes
4471           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4472                                        nextNod[0], nextNod[1], nextNod[2],
4473                                        prevNod[3], prevNod[4], prevNod[5],
4474                                        nextNod[3], nextNod[4], nextNod[5],
4475                                        midlNod[0], midlNod[1], midlNod[2]);
4476         }
4477         else if(nbSame==1) {
4478           // --->  2d order pyramid of 13 nodes
4479           int apex = iSameNode;
4480           int i0 = ( apex + 1 ) % nbCorners;
4481           int i1 = ( apex - 1 + nbCorners ) % nbCorners;
4482           int i0a = apex + 3;
4483           int i1a = i1 + 3;
4484           int i01 = i0 + 3;
4485           aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
4486                                       nextNod[i0], nextNod[i1], prevNod[apex],
4487                                       prevNod[i01], midlNod[i0],
4488                                       nextNod[i01], midlNod[i1],
4489                                       prevNod[i1a], prevNod[i0a],
4490                                       nextNod[i0a], nextNod[i1a]);
4491         }
4492         else if(nbSame==2) {
4493           // --->  2d order tetrahedron of 10 nodes
4494           int n1 = iNotSameNode;
4495           int n2 = ( n1 + 1             ) % nbCorners;
4496           int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
4497           int n12 = n1 + 3;
4498           int n23 = n2 + 3;
4499           int n31 = n3 + 3;
4500           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
4501                                        prevNod[n12], prevNod[n23], prevNod[n31],
4502                                        midlNod[n1], nextNod[n12], nextNod[n31]);
4503         }
4504         break;
4505       }
4506       case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
4507         if( nbSame == 0 ) {
4508           if ( nbDouble != 4 ) break;
4509           // --->  hexahedron with 20 nodes
4510           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4511                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4512                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4513                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4514                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
4515         }
4516         else if(nbSame==1) {
4517           // ---> pyramid + pentahedron - can not be created since it is needed
4518           // additional middle node at the center of face
4519           //INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
4520           return;
4521         }
4522         else if( nbSame == 2 ) {
4523           if ( nbDouble != 2 ) break;
4524           // --->  2d order Pentahedron with 15 nodes
4525           int n1,n2,n4,n5;
4526           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
4527             // iBeforeSame is same too
4528             n1 = iBeforeSame;
4529             n2 = iOpposSame;
4530             n4 = iSameNode;
4531             n5 = iAfterSame;
4532           }
4533           else {
4534             // iAfterSame is same too
4535             n1 = iSameNode;
4536             n2 = iBeforeSame;
4537             n4 = iAfterSame;
4538             n5 = iOpposSame;
4539           }
4540           int n12 = n2 + 4;
4541           int n45 = n4 + 4;
4542           int n14 = n1 + 4;
4543           int n25 = n5 + 4;
4544           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
4545                                        prevNod[n4], prevNod[n5], nextNod[n5],
4546                                        prevNod[n12], midlNod[n2], nextNod[n12],
4547                                        prevNod[n45], midlNod[n5], nextNod[n45],
4548                                        prevNod[n14], prevNod[n25], nextNod[n25]);
4549         }
4550         break;
4551       }
4552       case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
4553
4554         if( nbSame == 0 && nbDouble == 9 ) {
4555           // --->  tri-quadratic hexahedron with 27 nodes
4556           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4557                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4558                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4559                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4560                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4561                                        prevNod[8], // bottom center
4562                                        midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4563                                        nextNod[8], // top center
4564                                        midlNod[8]);// elem center
4565         }
4566         else
4567         {
4568           return;
4569         }
4570         break;
4571       }
4572       case SMDSEntity_Polygon: { // sweep POLYGON
4573
4574         if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4575           // --->  hexagonal prism
4576           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4577                                        prevNod[3], prevNod[4], prevNod[5],
4578                                        nextNod[0], nextNod[1], nextNod[2],
4579                                        nextNod[3], nextNod[4], nextNod[5]);
4580         }
4581         break;
4582       }
4583       case SMDSEntity_Ball:
4584         return;
4585
4586       default:
4587         break;
4588       } // switch ( baseType )
4589     } // scope
4590
4591     if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4592     {
4593       if ( baseType != SMDSEntity_Polygon )
4594       {
4595         const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType,nbNodes);
4596         SMDS_MeshCell::applyInterlace( ind, prevNod );
4597         SMDS_MeshCell::applyInterlace( ind, nextNod );
4598         SMDS_MeshCell::applyInterlace( ind, midlNod );
4599         SMDS_MeshCell::applyInterlace( ind, itNN );
4600         SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4601         baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4602       }
4603       vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4604       vector<int> quantities (nbNodes + 2);
4605       polyedre_nodes.clear();
4606       quantities.clear();
4607
4608       // bottom of prism
4609       for (int inode = 0; inode < nbNodes; inode++)
4610         polyedre_nodes.push_back( prevNod[inode] );
4611       quantities.push_back( nbNodes );
4612
4613       // top of prism
4614       polyedre_nodes.push_back( nextNod[0] );
4615       for (int inode = nbNodes; inode-1; --inode )
4616         polyedre_nodes.push_back( nextNod[inode-1] );
4617       quantities.push_back( nbNodes );
4618
4619       // side faces
4620       // 3--6--2
4621       // |     |
4622       // 7     5
4623       // |     |
4624       // 0--4--1
4625       const int iQuad = elem->IsQuadratic();
4626       for (int iface = 0; iface < nbNodes; iface += 1+iQuad )
4627       {
4628         const int prevNbNodes = polyedre_nodes.size(); // to detect degenerated face
4629         int inextface = (iface+1+iQuad) % nbNodes;
4630         int imid      = (iface+1) % nbNodes;
4631         polyedre_nodes.push_back( prevNod[inextface] );         // 0
4632         if ( iQuad ) polyedre_nodes.push_back( prevNod[imid] ); // 4
4633         polyedre_nodes.push_back( prevNod[iface] );             // 1
4634         if ( prevNod[iface] != nextNod[iface] ) // 1 != 2
4635         {
4636           if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]); // 5
4637           polyedre_nodes.push_back( nextNod[iface] );                         // 2
4638         }
4639         if ( iQuad ) polyedre_nodes.push_back( nextNod[imid] );               // 6
4640         if ( prevNod[inextface] != nextNod[inextface] ) // 0 != 3
4641         {
4642           polyedre_nodes.push_back( nextNod[inextface] );                            // 3
4643           if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);// 7
4644         }
4645         const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4646         if ( nbFaceNodes > 2 )
4647           quantities.push_back( nbFaceNodes );
4648         else // degenerated face
4649           polyedre_nodes.resize( prevNbNodes );
4650       }
4651       aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4652
4653     } // try to create a polyherdal prism
4654
4655     if ( aNewElem ) {
4656       newElems.push_back( aNewElem );
4657       myLastCreatedElems.push_back(aNewElem);
4658       srcElements.push_back( elem );
4659     }
4660
4661     // set new prev nodes
4662     for ( iNode = 0; iNode < nbNodes; iNode++ )
4663       prevNod[ iNode ] = nextNod[ iNode ];
4664
4665   } // loop on steps
4666 }
4667
4668 //=======================================================================
4669 /*!
4670  * \brief Create 1D and 2D elements around swept elements
4671  * \param mapNewNodes - source nodes and ones generated from them
4672  * \param newElemsMap - source elements and ones generated from them
4673  * \param elemNewNodesMap - nodes generated from each node of each element
4674  * \param elemSet - all swept elements
4675  * \param nbSteps - number of sweeping steps
4676  * \param srcElements - to append elem for each generated element
4677  */
4678 //=======================================================================
4679
4680 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap &     mapNewNodes,
4681                                   TTElemOfElemListMap &    newElemsMap,
4682                                   TElemOfVecOfNnlmiMap &   elemNewNodesMap,
4683                                   TIDSortedElemSet&        elemSet,
4684                                   const int                nbSteps,
4685                                   SMESH_SequenceOfElemPtr& srcElements)
4686 {
4687   ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4688   SMESHDS_Mesh* aMesh = GetMeshDS();
4689
4690   // Find nodes belonging to only one initial element - sweep them into edges.
4691
4692   TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4693   for ( ; nList != mapNewNodes.end(); nList++ )
4694   {
4695     const SMDS_MeshNode* node =
4696       static_cast<const SMDS_MeshNode*>( nList->first );
4697     if ( newElemsMap.count( node ))
4698       continue; // node was extruded into edge
4699     SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4700     int nbInitElems = 0;
4701     const SMDS_MeshElement* el = 0;
4702     SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4703     while ( eIt->more() && nbInitElems < 2 ) {
4704       const SMDS_MeshElement* e = eIt->next();
4705       SMDSAbs_ElementType  type = e->GetType();
4706       if ( type == SMDSAbs_Volume ||
4707            type < highType ||
4708            !elemSet.count(e))
4709         continue;
4710       if ( type > highType ) {
4711         nbInitElems = 0;
4712         highType    = type;
4713       }
4714       el = e;
4715       ++nbInitElems;
4716     }
4717     if ( nbInitElems == 1 ) {
4718       bool NotCreateEdge = el && el->IsMediumNode(node);
4719       if(!NotCreateEdge) {
4720         vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
4721         list<const SMDS_MeshElement*> newEdges;
4722         sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
4723       }
4724     }
4725   }
4726
4727   // Make a ceiling for each element ie an equal element of last new nodes.
4728   // Find free links of faces - make edges and sweep them into faces.
4729
4730   ElemFeatures polyFace( SMDSAbs_Face, /*isPoly=*/true ), anyFace;
4731
4732   TTElemOfElemListMap::iterator  itElem      = newElemsMap.begin();
4733   TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
4734   for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
4735   {
4736     const SMDS_MeshElement* elem = itElem->first;
4737     vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
4738
4739     if(itElem->second.size()==0) continue;
4740
4741     const bool isQuadratic = elem->IsQuadratic();
4742
4743     if ( elem->GetType() == SMDSAbs_Edge ) {
4744       // create a ceiling edge
4745       if ( !isQuadratic ) {
4746         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4747                                vecNewNodes[ 1 ]->second.back())) {
4748           myLastCreatedElems.push_back(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4749                                                       vecNewNodes[ 1 ]->second.back()));
4750           srcElements.push_back( elem );
4751         }
4752       }
4753       else {
4754         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4755                                vecNewNodes[ 1 ]->second.back(),
4756                                vecNewNodes[ 2 ]->second.back())) {
4757           myLastCreatedElems.push_back(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4758                                                       vecNewNodes[ 1 ]->second.back(),
4759                                                       vecNewNodes[ 2 ]->second.back()));
4760           srcElements.push_back( elem );
4761         }
4762       }
4763     }
4764     if ( elem->GetType() != SMDSAbs_Face )
4765       continue;
4766
4767     bool hasFreeLinks = false;
4768
4769     TIDSortedElemSet avoidSet;
4770     avoidSet.insert( elem );
4771
4772     set<const SMDS_MeshNode*> aFaceLastNodes;
4773     int iNode, nbNodes = vecNewNodes.size();
4774     if ( !isQuadratic ) {
4775       // loop on the face nodes
4776       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4777         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4778         // look for free links of the face
4779         int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
4780         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4781         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4782         // check if a link n1-n2 is free
4783         if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
4784           hasFreeLinks = true;
4785           // make a new edge and a ceiling for a new edge
4786           const SMDS_MeshElement* edge;
4787           if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
4788             myLastCreatedElems.push_back( edge = aMesh->AddEdge( n1, n2 )); // free link edge
4789             srcElements.push_back( myLastCreatedElems.back() );
4790           }
4791           n1 = vecNewNodes[ iNode ]->second.back();
4792           n2 = vecNewNodes[ iNext ]->second.back();
4793           if ( !aMesh->FindEdge( n1, n2 )) {
4794             myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2 )); // new edge ceiling
4795             srcElements.push_back( edge );
4796           }
4797         }
4798       }
4799     }
4800     else { // elem is quadratic face
4801       int nbn = nbNodes/2;
4802       for ( iNode = 0; iNode < nbn; iNode++ ) {
4803         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4804         int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
4805         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4806         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4807         const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
4808         // check if a link is free
4809         if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
4810              ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
4811              ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
4812           hasFreeLinks = true;
4813           // make an edge and a ceiling for a new edge
4814           // find medium node
4815           if ( !aMesh->FindEdge( n1, n2, n3 )) {
4816             myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2, n3 )); // free link edge
4817             srcElements.push_back( elem );
4818           }
4819           n1 = vecNewNodes[ iNode ]->second.back();
4820           n2 = vecNewNodes[ iNext ]->second.back();
4821           n3 = vecNewNodes[ iNode+nbn ]->second.back();
4822           if ( !aMesh->FindEdge( n1, n2, n3 )) {
4823             myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
4824             srcElements.push_back( elem );
4825           }
4826         }
4827       }
4828       for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
4829         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4830       }
4831     }
4832
4833     // sweep free links into faces
4834
4835     if ( hasFreeLinks ) {
4836       list<const SMDS_MeshElement*> & newVolumes = itElem->second;
4837       int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
4838
4839       set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
4840       set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
4841       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4842         initNodeSet.insert( vecNewNodes[ iNode ]->first );
4843         topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
4844       }
4845       if ( isQuadratic && nbNodes % 2 ) {  // node set for the case of a biquadratic
4846         initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
4847         initNodeSetNoCenter.erase( vecNewNodes.back()->first );
4848       }
4849       for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
4850         list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
4851         std::advance( v, volNb );
4852         // find indices of free faces of a volume and their source edges
4853         list< int > freeInd;
4854         list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
4855         SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
4856         int iF, nbF = vTool.NbFaces();
4857         for ( iF = 0; iF < nbF; iF ++ ) {
4858           if ( vTool.IsFreeFace( iF ) &&
4859                vTool.GetFaceNodes( iF, faceNodeSet ) &&
4860                initNodeSet != faceNodeSet) // except an initial face
4861           {
4862             if ( nbSteps == 1 && faceNodeSet == topNodeSet )
4863               continue;
4864             if ( faceNodeSet == initNodeSetNoCenter )
4865               continue;
4866             freeInd.push_back( iF );
4867             // find source edge of a free face iF
4868             vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
4869             vector<const SMDS_MeshNode*>::iterator lastCommom;
4870             commonNodes.resize( nbNodes, 0 );
4871             lastCommom = std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
4872                                                 initNodeSet.begin(), initNodeSet.end(),
4873                                                 commonNodes.begin());
4874             if ( std::distance( commonNodes.begin(), lastCommom ) == 3 )
4875               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
4876             else
4877               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
4878 #ifdef _DEBUG_
4879             if ( !srcEdges.back() )
4880             {
4881               cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
4882                    << iF << " of volume #" << vTool.ID() << endl;
4883             }
4884 #endif
4885           }
4886         }
4887         if ( freeInd.empty() )
4888           continue;
4889
4890         // create wall faces for all steps;
4891         // if such a face has been already created by sweep of edge,
4892         // assure that its orientation is OK
4893         for ( int iStep = 0; iStep < nbSteps; iStep++ )
4894         {
4895           vTool.Set( *v, /*ignoreCentralNodes=*/false );
4896           vTool.SetExternalNormal();
4897           const int nextShift = vTool.IsForward() ? +1 : -1;
4898           list< int >::iterator ind = freeInd.begin();
4899           list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
4900           for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
4901           {
4902             const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
4903             int nbn = vTool.NbFaceNodes( *ind );
4904             const SMDS_MeshElement * f = 0;
4905             if ( nbn == 3 )              ///// triangle
4906             {
4907               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
4908               if ( !f ||
4909                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4910               {
4911                 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
4912                                                      nodes[ 1 ],
4913                                                      nodes[ 1 + nextShift ] };
4914                 if ( f )
4915                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4916                 else
4917                   myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4918                                                                newOrder[ 2 ] ));
4919               }
4920             }
4921             else if ( nbn == 4 )       ///// quadrangle
4922             {
4923               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
4924               if ( !f ||
4925                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4926               {
4927                 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
4928                                                      nodes[ 2 ], nodes[ 2+nextShift ] };
4929                 if ( f )
4930                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4931                 else
4932                   myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4933                                                                newOrder[ 2 ], newOrder[ 3 ]));
4934               }
4935             }
4936             else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
4937             {
4938               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
4939               if ( !f ||
4940                    nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
4941               {
4942                 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
4943                                                      nodes[2],
4944                                                      nodes[2 + 2*nextShift],
4945                                                      nodes[3 - 2*nextShift],
4946                                                      nodes[3],
4947                                                      nodes[3 + 2*nextShift]};
4948                 if ( f )
4949                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4950                 else
4951                   myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ],
4952                                                                newOrder[ 1 ],
4953                                                                newOrder[ 2 ],
4954                                                                newOrder[ 3 ],
4955                                                                newOrder[ 4 ],
4956                                                                newOrder[ 5 ] ));
4957               }
4958             }
4959             else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
4960             {
4961               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
4962                                    nodes[1], nodes[3], nodes[5], nodes[7] );
4963               if ( !f ||
4964                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4965               {
4966                 const SMDS_MeshNode* newOrder[8] = { nodes[0],
4967                                                      nodes[4 - 2*nextShift],
4968                                                      nodes[4],
4969                                                      nodes[4 + 2*nextShift],
4970                                                      nodes[1],
4971                                                      nodes[5 - 2*nextShift],
4972                                                      nodes[5],
4973                                                      nodes[5 + 2*nextShift] };
4974                 if ( f )
4975                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4976                 else
4977                   myLastCreatedElems.push_back(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4978                                                               newOrder[ 2 ], newOrder[ 3 ],
4979                                                               newOrder[ 4 ], newOrder[ 5 ],
4980                                                               newOrder[ 6 ], newOrder[ 7 ]));
4981               }
4982             }
4983             else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
4984             {
4985               f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
4986                                       SMDSAbs_Face, /*noMedium=*/false);
4987               if ( !f ||
4988                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4989               {
4990                 const SMDS_MeshNode* newOrder[9] = { nodes[0],
4991                                                      nodes[4 - 2*nextShift],
4992                                                      nodes[4],
4993                                                      nodes[4 + 2*nextShift],
4994                                                      nodes[1],
4995                                                      nodes[5 - 2*nextShift],
4996                                                      nodes[5],
4997                                                      nodes[5 + 2*nextShift],
4998                                                      nodes[8] };
4999                 if ( f )
5000                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5001                 else
5002                   myLastCreatedElems.push_back(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5003                                                               newOrder[ 2 ], newOrder[ 3 ],
5004                                                               newOrder[ 4 ], newOrder[ 5 ],
5005                                                               newOrder[ 6 ], newOrder[ 7 ],
5006                                                               newOrder[ 8 ]));
5007               }
5008             }
5009             else  //////// polygon
5010             {
5011               vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
5012               const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
5013               if ( !f ||
5014                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
5015               {
5016                 if ( !vTool.IsForward() )
5017                   std::reverse( polygon_nodes.begin(), polygon_nodes.end());
5018                 if ( f )
5019                   aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
5020                 else
5021                   AddElement( polygon_nodes, polyFace.SetQuad( (*v)->IsQuadratic() ));
5022               }
5023             }
5024
5025             while ( srcElements.size() < myLastCreatedElems.size() )
5026               srcElements.push_back( *srcEdge );
5027
5028           }  // loop on free faces
5029
5030           // go to the next volume
5031           iVol = 0;
5032           while ( iVol++ < nbVolumesByStep ) v++;
5033
5034         } // loop on steps
5035       } // loop on volumes of one step
5036     } // sweep free links into faces
5037
5038     // Make a ceiling face with a normal external to a volume
5039
5040     // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
5041     SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
5042     int iF = lastVol.GetFaceIndex( aFaceLastNodes );
5043
5044     if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
5045       aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
5046       iF = lastVol.GetFaceIndex( aFaceLastNodes );
5047     }
5048     if ( iF >= 0 )
5049     {
5050       lastVol.SetExternalNormal();
5051       const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
5052       const               int nbn = lastVol.NbFaceNodes( iF );
5053       vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
5054       if ( !hasFreeLinks ||
5055            !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
5056       {
5057         const vector<int>& interlace =
5058           SMDS_MeshCell::interlacedSmdsOrder( elem->GetEntityType(), nbn );
5059         SMDS_MeshCell::applyInterlaceRev( interlace, nodeVec );
5060
5061         AddElement( nodeVec, anyFace.Init( elem ));
5062
5063         while ( srcElements.size() < myLastCreatedElems.size() )
5064           srcElements.push_back( elem );
5065       }
5066     }
5067   } // loop on swept elements
5068 }
5069
5070 //=======================================================================
5071 //function : RotationSweep
5072 //purpose  :
5073 //=======================================================================
5074
5075 SMESH_MeshEditor::PGroupIDs
5076 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet   theElemSets[2],
5077                                 const gp_Ax1&      theAxis,
5078                                 const double       theAngle,
5079                                 const int          theNbSteps,
5080                                 const double       theTol,
5081                                 const bool         theMakeGroups,
5082                                 const bool         theMakeWalls)
5083 {
5084   ClearLastCreated();
5085
5086   setElemsFirst( theElemSets );
5087   myLastCreatedElems.reserve( theElemSets[0].size() * theNbSteps );
5088   myLastCreatedNodes.reserve( theElemSets[1].size() * theNbSteps );
5089
5090   // source elements for each generated one
5091   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5092   srcElems.reserve( theElemSets[0].size() );
5093   srcNodes.reserve( theElemSets[1].size() );
5094
5095   gp_Trsf aTrsf;
5096   aTrsf.SetRotation( theAxis, theAngle );
5097   gp_Trsf aTrsf2;
5098   aTrsf2.SetRotation( theAxis, theAngle/2. );
5099
5100   gp_Lin aLine( theAxis );
5101   double aSqTol = theTol * theTol;
5102
5103   SMESHDS_Mesh* aMesh = GetMeshDS();
5104
5105   TNodeOfNodeListMap mapNewNodes;
5106   TElemOfVecOfNnlmiMap mapElemNewNodes;
5107   TTElemOfElemListMap newElemsMap;
5108
5109   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5110                                      myMesh->NbFaces(ORDER_QUADRATIC) +
5111                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
5112   // loop on theElemSets
5113   TIDSortedElemSet::iterator itElem;
5114   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5115   {
5116     TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5117     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5118       const SMDS_MeshElement* elem = *itElem;
5119       if ( !elem || elem->GetType() == SMDSAbs_Volume )
5120         continue;
5121       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5122       newNodesItVec.reserve( elem->NbNodes() );
5123
5124       // loop on elem nodes
5125       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5126       while ( itN->more() )
5127       {
5128         const SMDS_MeshNode* node = cast2Node( itN->next() );
5129
5130         gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
5131         double coord[3];
5132         aXYZ.Coord( coord[0], coord[1], coord[2] );
5133         bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
5134
5135         // check if a node has been already sweeped
5136         TNodeOfNodeListMapItr nIt =
5137           mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5138         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5139         if ( listNewNodes.empty() )
5140         {
5141           // check if we are to create medium nodes between corner ones
5142           bool needMediumNodes = false;
5143           if ( isQuadraticMesh )
5144           {
5145             SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5146             while (it->more() && !needMediumNodes )
5147             {
5148               const SMDS_MeshElement* invElem = it->next();
5149               if ( invElem != elem && !theElems.count( invElem )) continue;
5150               needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5151               if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5152                 needMediumNodes = true;
5153             }
5154           }
5155
5156           // make new nodes
5157           const SMDS_MeshNode * newNode = node;
5158           for ( int i = 0; i < theNbSteps; i++ ) {
5159             if ( !isOnAxis ) {
5160               if ( needMediumNodes )  // create a medium node
5161               {
5162                 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5163                 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5164                 myLastCreatedNodes.push_back(newNode);
5165                 srcNodes.push_back( node );
5166                 listNewNodes.push_back( newNode );
5167                 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5168               }
5169               else {
5170                 aTrsf.Transforms( coord[0], coord[1], coord[2] );
5171               }
5172               // create a corner node
5173               newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5174               myLastCreatedNodes.push_back(newNode);
5175               srcNodes.push_back( node );
5176               listNewNodes.push_back( newNode );
5177             }
5178             else {
5179               listNewNodes.push_back( newNode );
5180               // if ( needMediumNodes )
5181               //   listNewNodes.push_back( newNode );
5182             }
5183           }
5184         }
5185         newNodesItVec.push_back( nIt );
5186       }
5187       // make new elements
5188       sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
5189     }
5190   }
5191
5192   if ( theMakeWalls )
5193     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], theNbSteps, srcElems );
5194
5195   PGroupIDs newGroupIDs;
5196   if ( theMakeGroups )
5197     newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
5198
5199   return newGroupIDs;
5200 }
5201
5202 //=======================================================================
5203 //function : ExtrusParam
5204 //purpose  : standard construction
5205 //=======================================================================
5206
5207 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Vec&            theStep,
5208                                             const int                theNbSteps,
5209                                             const std::list<double>& theScales,
5210                                             const std::list<double>& theAngles,
5211                                             const gp_XYZ*            theBasePoint,
5212                                             const int                theFlags,
5213                                             const double             theTolerance):
5214   myDir( theStep ),
5215   myBaseP( Precision::Infinite(), 0, 0 ),
5216   myFlags( theFlags ),
5217   myTolerance( theTolerance ),
5218   myElemsToUse( NULL )
5219 {
5220   mySteps = new TColStd_HSequenceOfReal;
5221   const double stepSize = theStep.Magnitude();
5222   for (int i=1; i<=theNbSteps; i++ )
5223     mySteps->Append( stepSize );
5224
5225   if ( !theScales.empty() )
5226   {
5227     if ( IsScaleVariation() && (int)theScales.size() < theNbSteps )
5228       linearScaleVariation( theNbSteps, const_cast< std::list<double>& >( theScales ));
5229
5230     // add medium scales
5231     std::list<double>::const_iterator s2 = theScales.begin(), s1 = s2++;
5232     myScales.reserve( theNbSteps * 2 );
5233     myScales.push_back( 0.5 * ( *s1 + 1. ));
5234     myScales.push_back( *s1 );
5235     for ( ; s2 != theScales.end(); s1 = s2++ )
5236     {
5237       myScales.push_back( 0.5 * ( *s1 + *s2 ));
5238       myScales.push_back( *s2 );
5239     }
5240   }
5241
5242   if ( !theAngles.empty() )
5243   {
5244     std::list<double>& angles = const_cast< std::list<double>& >( theAngles );
5245     if ( IsAngleVariation() && (int)theAngles.size() < theNbSteps )
5246       linearAngleVariation( theNbSteps, angles );
5247
5248     // accumulate angles
5249     double angle = 0;
5250     int nbAngles = 0;
5251     std::list<double>::iterator a1 = angles.begin(), a2;
5252     for ( ; a1 != angles.end(); ++a1, ++nbAngles )
5253     {
5254       angle += *a1;
5255       *a1 = angle;
5256     }
5257     while ( nbAngles++ < theNbSteps )
5258       angles.push_back( angles.back() );
5259
5260     // add medium angles
5261     a2 = angles.begin(), a1 = a2++;
5262     myAngles.push_back( 0.5 * *a1 );
5263     myAngles.push_back( *a1 );
5264     for ( ; a2 != angles.end(); a1 = a2++ )
5265     {
5266       myAngles.push_back( 0.5 * ( *a1 + *a2 ));
5267       myAngles.push_back( *a2 );
5268     }
5269   }
5270
5271   if ( theBasePoint )
5272   {
5273     myBaseP = *theBasePoint;
5274   }
5275
5276   if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5277       ( theTolerance > 0 ))
5278   {
5279     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5280   }
5281   else
5282   {
5283     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5284   }
5285 }
5286
5287 //=======================================================================
5288 //function : ExtrusParam
5289 //purpose  : steps are given explicitly
5290 //=======================================================================
5291
5292 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Dir&                   theDir,
5293                                             Handle(TColStd_HSequenceOfReal) theSteps,
5294                                             const int                       theFlags,
5295                                             const double                    theTolerance):
5296   myDir( theDir ),
5297   mySteps( theSteps ),
5298   myFlags( theFlags ),
5299   myTolerance( theTolerance ),
5300   myElemsToUse( NULL )
5301 {
5302   if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5303       ( theTolerance > 0 ))
5304   {
5305     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5306   }
5307   else
5308   {
5309     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5310   }
5311 }
5312
5313 //=======================================================================
5314 //function : ExtrusParam
5315 //purpose  : for extrusion by normal
5316 //=======================================================================
5317
5318 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const double theStepSize,
5319                                             const int    theNbSteps,
5320                                             const int    theFlags,
5321                                             const int    theDim ):
5322   myDir( 1,0,0 ),
5323   mySteps( new TColStd_HSequenceOfReal ),
5324   myFlags( theFlags ),
5325   myTolerance( 0 ),
5326   myElemsToUse( NULL )
5327 {
5328   for (int i = 0; i < theNbSteps; i++ )
5329     mySteps->Append( theStepSize );
5330
5331   if ( theDim == 1 )
5332   {
5333     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal1D;
5334   }
5335   else
5336   {
5337     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal2D;
5338   }
5339 }
5340
5341 //=======================================================================
5342 //function : ExtrusParam
5343 //purpose  : for extrusion along path
5344 //=======================================================================
5345
5346 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const std::vector< PathPoint >& thePoints,
5347                                             const gp_Pnt*                   theBasePoint,
5348                                             const std::list<double>&        theScales,
5349                                             const bool                      theMakeGroups )
5350   : myBaseP( Precision::Infinite(), 0, 0 ),
5351     myFlags( EXTRUSION_FLAG_BOUNDARY | ( theMakeGroups ? EXTRUSION_FLAG_GROUPS : 0 )),
5352     myPathPoints( thePoints )
5353 {
5354   if ( theBasePoint )
5355   {
5356     myBaseP = theBasePoint->XYZ();
5357   }
5358
5359   if ( !theScales.empty() )
5360   {
5361     // add medium scales
5362     std::list<double>::const_iterator s2 = theScales.begin(), s1 = s2++;
5363     myScales.reserve( thePoints.size() * 2 );
5364     myScales.push_back( 0.5 * ( 1. + *s1 ));
5365     myScales.push_back( *s1 );
5366     for ( ; s2 != theScales.end(); s1 = s2++ )
5367     {
5368       myScales.push_back( 0.5 * ( *s1 + *s2 ));
5369       myScales.push_back( *s2 );
5370     }
5371   }
5372
5373   myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesAlongTrack;
5374 }
5375
5376 //=======================================================================
5377 //function : ExtrusParam::SetElementsToUse
5378 //purpose  : stores elements to use for extrusion by normal, depending on
5379 //           state of EXTRUSION_FLAG_USE_INPUT_ELEMS_ONLY flag;
5380 //           define myBaseP for scaling
5381 //=======================================================================
5382
5383 void SMESH_MeshEditor::ExtrusParam::SetElementsToUse( const TIDSortedElemSet& elems,
5384                                                       const TIDSortedElemSet& nodes )
5385 {
5386   myElemsToUse = ToUseInpElemsOnly() ? & elems : 0;
5387
5388   if ( Precision::IsInfinite( myBaseP.X() )) // myBaseP not defined
5389   {
5390     myBaseP.SetCoord( 0.,0.,0. );
5391     TIDSortedElemSet newNodes;
5392
5393     const TIDSortedElemSet* elemSets[] = { &elems, &nodes };
5394     for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5395     {
5396       const TIDSortedElemSet& elements = *( elemSets[ is2ndSet ]);
5397       TIDSortedElemSet::const_iterator itElem = elements.begin();
5398       for ( ; itElem != elements.end(); itElem++ )
5399       {
5400         const SMDS_MeshElement* elem = *itElem;
5401         SMDS_ElemIteratorPtr     itN = elem->nodesIterator();
5402         while ( itN->more() ) {
5403           const SMDS_MeshElement* node = itN->next();
5404           if ( newNodes.insert( node ).second )
5405             myBaseP += SMESH_NodeXYZ( node );
5406         }
5407       }
5408     }
5409     myBaseP /= newNodes.size();
5410   }
5411 }
5412
5413 //=======================================================================
5414 //function : ExtrusParam::beginStepIter
5415 //purpose  : prepare iteration on steps
5416 //=======================================================================
5417
5418 void SMESH_MeshEditor::ExtrusParam::beginStepIter( bool withMediumNodes )
5419 {
5420   myWithMediumNodes = withMediumNodes;
5421   myNextStep = 1;
5422   myCurSteps.clear();
5423 }
5424 //=======================================================================
5425 //function : ExtrusParam::moreSteps
5426 //purpose  : are there more steps?
5427 //=======================================================================
5428
5429 bool SMESH_MeshEditor::ExtrusParam::moreSteps()
5430 {
5431   return myNextStep <= mySteps->Length() || !myCurSteps.empty();
5432 }
5433 //=======================================================================
5434 //function : ExtrusParam::nextStep
5435 //purpose  : returns the next step
5436 //=======================================================================
5437
5438 double SMESH_MeshEditor::ExtrusParam::nextStep()
5439 {
5440   double res = 0;
5441   if ( !myCurSteps.empty() )
5442   {
5443     res = myCurSteps.back();
5444     myCurSteps.pop_back();
5445   }
5446   else if ( myNextStep <= mySteps->Length() )
5447   {
5448     myCurSteps.push_back( mySteps->Value( myNextStep ));
5449     ++myNextStep;
5450     if ( myWithMediumNodes )
5451     {
5452       myCurSteps.back() /= 2.;
5453       myCurSteps.push_back( myCurSteps.back() );
5454     }
5455     res = nextStep();
5456   }
5457   return res;
5458 }
5459
5460 //=======================================================================
5461 //function : ExtrusParam::makeNodesByDir
5462 //purpose  : create nodes for standard extrusion
5463 //=======================================================================
5464
5465 int SMESH_MeshEditor::ExtrusParam::
5466 makeNodesByDir( SMESHDS_Mesh*                     mesh,
5467                 const SMDS_MeshNode*              srcNode,
5468                 std::list<const SMDS_MeshNode*> & newNodes,
5469                 const bool                        makeMediumNodes)
5470 {
5471   gp_XYZ p = SMESH_NodeXYZ( srcNode );
5472
5473   int nbNodes = 0;
5474   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5475   {
5476     p += myDir.XYZ() * nextStep();
5477     const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5478     newNodes.push_back( newNode );
5479   }
5480
5481   if ( !myScales.empty() || !myAngles.empty() )
5482   {
5483     gp_XYZ  center = myBaseP;
5484     gp_Ax1  ratationAxis( center, myDir );
5485     gp_Trsf rotation;
5486
5487     std::list<const SMDS_MeshNode*>::iterator nIt = newNodes.begin();
5488     size_t i = !makeMediumNodes;
5489     for ( beginStepIter( makeMediumNodes );
5490           moreSteps();
5491           ++nIt, i += 1 + !makeMediumNodes )
5492     {
5493       center += myDir.XYZ() * nextStep();
5494
5495       gp_XYZ xyz = SMESH_NodeXYZ( *nIt );
5496       bool moved = false;
5497       if ( i < myScales.size() )
5498       {
5499         xyz = ( myScales[i] * ( xyz - center )) + center;
5500         moved = true;
5501       }
5502       if ( !myAngles.empty() )
5503       {
5504         rotation.SetRotation( ratationAxis, myAngles[i] );
5505         rotation.Transforms( xyz );
5506         moved = true;
5507       }
5508       if ( moved )
5509         mesh->MoveNode( *nIt, xyz.X(), xyz.Y(), xyz.Z() );
5510       else
5511         break;
5512     }
5513   }
5514   return nbNodes;
5515 }
5516
5517 //=======================================================================
5518 //function : ExtrusParam::makeNodesByDirAndSew
5519 //purpose  : create nodes for standard extrusion with sewing
5520 //=======================================================================
5521
5522 int SMESH_MeshEditor::ExtrusParam::
5523 makeNodesByDirAndSew( SMESHDS_Mesh*                     mesh,
5524                       const SMDS_MeshNode*              srcNode,
5525                       std::list<const SMDS_MeshNode*> & newNodes,
5526                       const bool                        makeMediumNodes)
5527 {
5528   gp_XYZ P1 = SMESH_NodeXYZ( srcNode );
5529
5530   int nbNodes = 0;
5531   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5532   {
5533     P1 += myDir.XYZ() * nextStep();
5534
5535     // try to search in sequence of existing nodes
5536     // if myNodes.size()>0 we 'nave to use given sequence
5537     // else - use all nodes of mesh
5538     const SMDS_MeshNode * node = 0;
5539     if ( myNodes.Length() > 0 )
5540     {
5541       for ( int i = 1; i <= myNodes.Length(); i++ )
5542       {
5543         SMESH_NodeXYZ P2 = myNodes.Value(i);
5544         if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5545         {
5546           node = myNodes.Value(i);
5547           break;
5548         }
5549       }
5550     }
5551     else
5552     {
5553       SMDS_NodeIteratorPtr itn = mesh->nodesIterator();
5554       while(itn->more())
5555       {
5556         SMESH_NodeXYZ P2 = itn->next();
5557         if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5558         {
5559           node = P2._node;
5560           break;
5561         }
5562       }
5563     }
5564
5565     if ( !node )
5566       node = mesh->AddNode( P1.X(), P1.Y(), P1.Z() );
5567
5568     newNodes.push_back( node );
5569
5570   } // loop on steps
5571
5572   return nbNodes;
5573 }
5574
5575 //=======================================================================
5576 //function : ExtrusParam::makeNodesByNormal2D
5577 //purpose  : create nodes for extrusion using normals of faces
5578 //=======================================================================
5579
5580 int SMESH_MeshEditor::ExtrusParam::
5581 makeNodesByNormal2D( SMESHDS_Mesh*                     mesh,
5582                      const SMDS_MeshNode*              srcNode,
5583                      std::list<const SMDS_MeshNode*> & newNodes,
5584                      const bool                        makeMediumNodes)
5585 {
5586   const bool alongAvgNorm = ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL );
5587
5588   gp_XYZ p = SMESH_NodeXYZ( srcNode );
5589
5590   // get normals to faces sharing srcNode
5591   vector< gp_XYZ > norms, baryCenters;
5592   gp_XYZ norm, avgNorm( 0,0,0 );
5593   SMDS_ElemIteratorPtr faceIt = srcNode->GetInverseElementIterator( SMDSAbs_Face );
5594   while ( faceIt->more() )
5595   {
5596     const SMDS_MeshElement* face = faceIt->next();
5597     if ( myElemsToUse && !myElemsToUse->count( face ))
5598       continue;
5599     if ( SMESH_MeshAlgos::FaceNormal( face, norm, /*normalized=*/true ))
5600     {
5601       norms.push_back( norm );
5602       avgNorm += norm;
5603       if ( !alongAvgNorm )
5604       {
5605         gp_XYZ bc(0,0,0);
5606         int nbN = 0;
5607         for ( SMDS_ElemIteratorPtr nIt = face->nodesIterator(); nIt->more(); ++nbN )
5608           bc += SMESH_NodeXYZ( nIt->next() );
5609         baryCenters.push_back( bc / nbN );
5610       }
5611     }
5612   }
5613
5614   if ( norms.empty() ) return 0;
5615
5616   double normSize = avgNorm.Modulus();
5617   if ( normSize < std::numeric_limits<double>::min() )
5618     return 0;
5619
5620   if ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL ) // extrude along avgNorm
5621   {
5622     myDir = avgNorm;
5623     return makeNodesByDir( mesh, srcNode, newNodes, makeMediumNodes );
5624   }
5625
5626   avgNorm /= normSize;
5627
5628   int nbNodes = 0;
5629   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5630   {
5631     gp_XYZ pNew = p;
5632     double stepSize = nextStep();
5633
5634     if ( norms.size() > 1 )
5635     {
5636       for ( size_t iF = 0; iF < norms.size(); ++iF ) // loop on faces
5637       {
5638         // translate plane of a face
5639         baryCenters[ iF ] += norms[ iF ] * stepSize;
5640
5641         // find point of intersection of the face plane located at baryCenters[ iF ]
5642         // and avgNorm located at pNew
5643         double d    = -( norms[ iF ] * baryCenters[ iF ]); // d of plane equation ax+by+cz+d=0
5644         double dot  = ( norms[ iF ] * avgNorm );
5645         if ( dot < std::numeric_limits<double>::min() )
5646           dot = stepSize * 1e-3;
5647         double step = -( norms[ iF ] * pNew + d ) / dot;
5648         pNew += step * avgNorm;
5649       }
5650     }
5651     else
5652     {
5653       pNew += stepSize * avgNorm;
5654     }
5655     p = pNew;
5656
5657     const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5658     newNodes.push_back( newNode );
5659   }
5660   return nbNodes;
5661 }
5662
5663 //=======================================================================
5664 //function : ExtrusParam::makeNodesByNormal1D
5665 //purpose  : create nodes for extrusion using normals of edges
5666 //=======================================================================
5667
5668 int SMESH_MeshEditor::ExtrusParam::
5669 makeNodesByNormal1D( SMESHDS_Mesh*                     mesh,
5670                      const SMDS_MeshNode*              srcNode,
5671                      std::list<const SMDS_MeshNode*> & newNodes,
5672                      const bool                        makeMediumNodes)
5673 {
5674   throw SALOME_Exception("Extrusion 1D by Normal not implemented");
5675   return 0;
5676 }
5677
5678 //=======================================================================
5679 //function : ExtrusParam::makeNodesAlongTrack
5680 //purpose  : create nodes for extrusion along path
5681 //=======================================================================
5682
5683 int SMESH_MeshEditor::ExtrusParam::
5684 makeNodesAlongTrack( SMESHDS_Mesh*                     mesh,
5685                      const SMDS_MeshNode*              srcNode,
5686                      std::list<const SMDS_MeshNode*> & newNodes,
5687                      const bool                        makeMediumNodes)
5688 {
5689   const Standard_Real aTolAng=1.e-4;
5690
5691   gp_Pnt aV0x = myBaseP;
5692   gp_Pnt aPN0 = SMESH_NodeXYZ( srcNode );
5693
5694   const PathPoint& aPP0 = myPathPoints[0];
5695   gp_Pnt aP0x = aPP0.myPnt;
5696   gp_Dir aDT0x= aPP0.myTgt;
5697
5698   std::vector< gp_Pnt > centers;
5699   centers.reserve( NbSteps() * 2 );
5700
5701   gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
5702
5703   for ( size_t j = 1; j < myPathPoints.size(); ++j )
5704   {
5705     const PathPoint&  aPP  = myPathPoints[j];
5706     const gp_Pnt&     aP1x = aPP.myPnt;
5707     const gp_Dir&    aDT1x = aPP.myTgt;
5708
5709     // Translation
5710     gp_Vec aV01x( aP0x, aP1x );
5711     aTrsf.SetTranslation( aV01x );
5712     gp_Pnt aV1x = aV0x.Transformed( aTrsf );
5713     gp_Pnt aPN1 = aPN0.Transformed( aTrsf );
5714
5715     // rotation 1 [ T1,T0 ]
5716     Standard_Real aAngleT1T0 = -aDT1x.Angle( aDT0x );
5717     if ( fabs( aAngleT1T0 ) > aTolAng )
5718     {
5719       gp_Dir aDT1T0 = aDT1x ^ aDT0x;
5720       aTrsfRotT1T0.SetRotation( gp_Ax1( aV1x, aDT1T0 ), aAngleT1T0 );
5721
5722       aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
5723     }
5724
5725     // rotation 2
5726     if ( aPP.myAngle != 0. )
5727     {
5728       aTrsfRot.SetRotation( gp_Ax1( aV1x, aDT1x ), aPP.myAngle );
5729       aPN1 = aPN1.Transformed( aTrsfRot );
5730     }
5731
5732     // make new node
5733     if ( makeMediumNodes )
5734     {
5735       // create additional node
5736       gp_XYZ midP = 0.5 * ( aPN1.XYZ() + aPN0.XYZ() );
5737       const SMDS_MeshNode* newNode = mesh->AddNode( midP.X(), midP.Y(), midP.Z() );
5738       newNodes.push_back( newNode );
5739
5740     }
5741     const SMDS_MeshNode* newNode = mesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
5742     newNodes.push_back( newNode );
5743
5744     centers.push_back( 0.5 * ( aV0x.XYZ() + aV1x.XYZ() ));
5745     centers.push_back( aV1x );
5746
5747     aPN0 = aPN1;
5748     aP0x = aP1x;
5749     aV0x = aV1x;
5750     aDT0x = aDT1x;
5751   }
5752
5753   // scale
5754   if ( !myScales.empty() )
5755   {
5756     gp_Trsf aTrsfScale;
5757     std::list<const SMDS_MeshNode*>::iterator node = newNodes.begin();
5758     for ( size_t i = !makeMediumNodes;
5759           i < myScales.size() && node != newNodes.end();
5760           i += ( 1 + !makeMediumNodes ), ++node )
5761     {
5762       aTrsfScale.SetScale( centers[ i ], myScales[ i ] );
5763       gp_Pnt aN = SMESH_NodeXYZ( *node );
5764       gp_Pnt aP = aN.Transformed( aTrsfScale );
5765       mesh->MoveNode( *node, aP.X(), aP.Y(), aP.Z() );
5766     }
5767   }
5768
5769   return myPathPoints.size() + makeMediumNodes * ( myPathPoints.size() - 2 );
5770 }
5771
5772 //=======================================================================
5773 //function : ExtrusionSweep
5774 //purpose  :
5775 //=======================================================================
5776
5777 SMESH_MeshEditor::PGroupIDs
5778 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet     theElems[2],
5779                                   const gp_Vec&        theStep,
5780                                   const int            theNbSteps,
5781                                   TTElemOfElemListMap& newElemsMap,
5782                                   const int            theFlags,
5783                                   const double         theTolerance)
5784 {
5785   std::list<double> dummy;
5786   ExtrusParam aParams( theStep, theNbSteps, dummy, dummy, 0,
5787                        theFlags, theTolerance );
5788   return ExtrusionSweep( theElems, aParams, newElemsMap );
5789 }
5790
5791 namespace
5792 {
5793
5794 //=======================================================================
5795 //function : getOriFactor
5796 //purpose  : Return -1 or 1 depending on if order of given nodes corresponds to
5797 //           edge curve orientation
5798 //=======================================================================
5799
5800   double getOriFactor( const TopoDS_Edge&   edge,
5801                        const SMDS_MeshNode* n1,
5802                        const SMDS_MeshNode* n2,
5803                        SMESH_MesherHelper&  helper)
5804   {
5805     double u1 = helper.GetNodeU( edge, n1, n2 );
5806     double u2 = helper.GetNodeU( edge, n2, n1 );
5807     return u1 < u2 ? 1. : -1.;
5808   }
5809 }
5810
5811 //=======================================================================
5812 //function : ExtrusionSweep
5813 //purpose  :
5814 //=======================================================================
5815
5816 SMESH_MeshEditor::PGroupIDs
5817 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet     theElemSets[2],
5818                                   ExtrusParam&         theParams,
5819                                   TTElemOfElemListMap& newElemsMap)
5820 {
5821   ClearLastCreated();
5822
5823   setElemsFirst( theElemSets );
5824   myLastCreatedElems.reserve( theElemSets[0].size() * theParams.NbSteps() );
5825   myLastCreatedNodes.reserve( theElemSets[1].size() * theParams.NbSteps() );
5826
5827   // source elements for each generated one
5828   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5829   srcElems.reserve( theElemSets[0].size() );
5830   srcNodes.reserve( theElemSets[1].size() );
5831
5832   const int nbSteps = theParams.NbSteps();
5833   theParams.SetElementsToUse( theElemSets[0], theElemSets[1] );
5834
5835   TNodeOfNodeListMap   mapNewNodes;
5836   TElemOfVecOfNnlmiMap mapElemNewNodes;
5837
5838   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5839                                      myMesh->NbFaces(ORDER_QUADRATIC) +
5840                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
5841   // loop on theElems
5842   TIDSortedElemSet::iterator itElem;
5843   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5844   {
5845     TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5846     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5847     {
5848       // check element type
5849       const SMDS_MeshElement* elem = *itElem;
5850       if ( !elem  || elem->GetType() == SMDSAbs_Volume )
5851         continue;
5852
5853       const size_t nbNodes = elem->NbNodes();
5854       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5855       newNodesItVec.reserve( nbNodes );
5856
5857       // loop on elem nodes
5858       SMDS_NodeIteratorPtr itN = elem->nodeIterator();
5859       while ( itN->more() )
5860       {
5861         // check if a node has been already sweeped
5862         const SMDS_MeshNode* node = itN->next();
5863         TNodeOfNodeListMap::iterator nIt =
5864           mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5865         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5866         if ( listNewNodes.empty() )
5867         {
5868           // make new nodes
5869
5870           // check if we are to create medium nodes between corner ones
5871           bool needMediumNodes = false;
5872           if ( isQuadraticMesh )
5873           {
5874             SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5875             while (it->more() && !needMediumNodes )
5876             {
5877               const SMDS_MeshElement* invElem = it->next();
5878               if ( invElem != elem && !theElems.count( invElem )) continue;
5879               needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5880               if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5881                 needMediumNodes = true;
5882             }
5883           }
5884           // create nodes for all steps
5885           if ( theParams.MakeNodes( GetMeshDS(), node, listNewNodes, needMediumNodes ))
5886           {
5887             list<const SMDS_MeshNode*>::iterator newNodesIt = listNewNodes.begin();
5888             for ( ; newNodesIt != listNewNodes.end(); ++newNodesIt )
5889             {
5890               myLastCreatedNodes.push_back( *newNodesIt );
5891               srcNodes.push_back( node );
5892             }
5893           }
5894           else
5895           {
5896             if ( theParams.ToMakeBoundary() )
5897             {
5898               GetMeshDS()->Modified();
5899               throw SALOME_Exception( SMESH_Comment("Can't extrude node #") << node->GetID() );
5900             }
5901             break; // newNodesItVec will be shorter than nbNodes
5902           }
5903         }
5904         newNodesItVec.push_back( nIt );
5905       }
5906       // make new elements
5907       if ( newNodesItVec.size() == nbNodes )
5908         sweepElement( elem, newNodesItVec, newElemsMap[elem], nbSteps, srcElems );
5909     }
5910   }
5911
5912   if ( theParams.ToMakeBoundary() ) {
5913     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], nbSteps, srcElems );
5914   }
5915   PGroupIDs newGroupIDs;
5916   if ( theParams.ToMakeGroups() )
5917     newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
5918
5919   return newGroupIDs;
5920 }
5921
5922 //=======================================================================
5923 //function : ExtrusionAlongTrack
5924 //purpose  :
5925 //=======================================================================
5926 SMESH_MeshEditor::Extrusion_Error
5927 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet     theElements[2],
5928                                        SMESH_Mesh*          theTrackMesh,
5929                                        SMDS_ElemIteratorPtr theTrackIterator,
5930                                        const SMDS_MeshNode* theN1,
5931                                        std::list<double>&   theAngles,
5932                                        const bool           theAngleVariation,
5933                                        std::list<double>&   theScales,
5934                                        const bool           theScaleVariation,
5935                                        const gp_Pnt*        theRefPoint,
5936                                        const bool           theMakeGroups)
5937 {
5938   ClearLastCreated();
5939
5940   // 1. Check data
5941   if ( theElements[0].empty() && theElements[1].empty() )
5942     return EXTR_NO_ELEMENTS;
5943
5944   ASSERT( theTrackMesh );
5945   if ( ! theTrackIterator || !theTrackIterator->more() )
5946     return EXTR_NO_ELEMENTS;
5947
5948   // 2. Get ordered nodes
5949   SMESH_MeshAlgos::TElemGroupVector branchEdges;
5950   SMESH_MeshAlgos::TNodeGroupVector branchNods;
5951   SMESH_MeshAlgos::Get1DBranches( theTrackIterator, branchEdges, branchNods, theN1 );
5952   if ( branchEdges.empty() )
5953     return EXTR_PATH_NOT_EDGE;
5954
5955   if ( branchEdges.size() > 1 )
5956     return EXTR_BAD_PATH_SHAPE;
5957
5958   std::vector< const SMDS_MeshNode* >&    pathNodes = branchNods[0];
5959   std::vector< const SMDS_MeshElement* >& pathEdges = branchEdges[0];
5960   if ( pathNodes[0] != theN1 && pathNodes[1] != theN1 )
5961     return EXTR_BAD_STARTING_NODE;
5962
5963   if ( theTrackMesh->NbEdges( ORDER_QUADRATIC ) > 0 )
5964   {
5965     // add medium nodes to pathNodes
5966     std::vector< const SMDS_MeshNode* >    pathNodes2;
5967     std::vector< const SMDS_MeshElement* > pathEdges2;
5968     pathNodes2.reserve( pathNodes.size() * 2 );
5969     pathEdges2.reserve( pathEdges.size() * 2 );
5970     for ( size_t i = 0; i < pathEdges.size(); ++i )
5971     {
5972       pathNodes2.push_back( pathNodes[i] );
5973       pathEdges2.push_back( pathEdges[i] );
5974       if ( pathEdges[i]->IsQuadratic() )
5975       {
5976         pathNodes2.push_back( pathEdges[i]->GetNode(2) );
5977         pathEdges2.push_back( pathEdges[i] );
5978       }
5979     }
5980     pathNodes2.push_back( pathNodes.back() );
5981     pathEdges.swap( pathEdges2 );
5982     pathNodes.swap( pathNodes2 );
5983   }
5984
5985   // 3. Get path data at pathNodes
5986
5987   std::vector< ExtrusParam::PathPoint > points( pathNodes.size() );
5988
5989   if ( theAngleVariation )
5990     linearAngleVariation( points.size()-1, theAngles );
5991   if ( theScaleVariation )
5992     linearScaleVariation( points.size()-1, theScales );
5993
5994   theAngles.push_front( 0 ); // for the 1st point that is not transformed
5995   std::list<double>::iterator angle = theAngles.begin();
5996
5997   SMESHDS_Mesh* pathMeshDS = theTrackMesh->GetMeshDS();
5998
5999   std::map< int, double > edgeID2OriFactor; // orientation of EDGEs
6000   std::map< int, double >::iterator id2factor;
6001   SMESH_MesherHelper pathHelper( *theTrackMesh );
6002   gp_Pnt p; gp_Vec tangent;
6003   const double tol2 = gp::Resolution() * gp::Resolution();
6004
6005   for ( size_t i = 0; i < pathNodes.size(); ++i )
6006   {
6007     ExtrusParam::PathPoint & point = points[ i ];
6008
6009     point.myPnt = SMESH_NodeXYZ( pathNodes[ i ]);
6010
6011     if ( angle != theAngles.end() )
6012       point.myAngle = *angle++;
6013
6014     tangent.SetCoord( 0,0,0 );
6015     const int          shapeID = pathNodes[ i ]->GetShapeID();
6016     const TopoDS_Shape&  shape = pathMeshDS->IndexToShape( shapeID );
6017     TopAbs_ShapeEnum shapeType = shape.IsNull() ? TopAbs_SHAPE : shape.ShapeType();
6018     switch ( shapeType )
6019     {
6020     case TopAbs_EDGE:
6021     {
6022       TopoDS_Edge edge = TopoDS::Edge( shape );
6023       id2factor = edgeID2OriFactor.insert( std::make_pair( shapeID, 0 )).first;
6024       if ( id2factor->second == 0 )
6025       {
6026         if ( i ) id2factor->second = getOriFactor( edge, pathNodes[i-1], pathNodes[i], pathHelper );
6027         else     id2factor->second = getOriFactor( edge, pathNodes[i], pathNodes[i+1], pathHelper );
6028       }
6029       double u = pathHelper.GetNodeU( edge, pathNodes[i] ), u0, u1;
6030       Handle(Geom_Curve) curve = BRep_Tool::Curve( edge, u0, u1 );
6031       curve->D1( u, p, tangent );
6032       tangent *= id2factor->second;
6033       break;
6034     }
6035     case TopAbs_VERTEX:
6036     {
6037       int nbEdges = 0;
6038       PShapeIteratorPtr shapeIt = pathHelper.GetAncestors( shape, *theTrackMesh, TopAbs_EDGE );
6039       while ( const TopoDS_Shape* edgePtr = shapeIt->next() )
6040       {
6041         int edgeID = pathMeshDS->ShapeToIndex( *edgePtr );
6042         for ( int di = -1; di <= 0; ++di )
6043         {
6044           size_t j = i + di;
6045           if ( j < pathEdges.size() && edgeID == pathEdges[ j ]->GetShapeID() )
6046           {
6047             TopoDS_Edge edge = TopoDS::Edge( *edgePtr );
6048             id2factor = edgeID2OriFactor.insert( std::make_pair( edgeID, 0 )).first;
6049             if ( id2factor->second == 0 )
6050             {
6051               if ( j < i )
6052                 id2factor->second = getOriFactor( edge, pathNodes[i-1], pathNodes[i], pathHelper );
6053               else
6054                 id2factor->second = getOriFactor( edge, pathNodes[i], pathNodes[i+1], pathHelper );
6055             }
6056             double u = pathHelper.GetNodeU( edge, pathNodes[i] ), u0, u1;
6057             Handle(Geom_Curve) curve = BRep_Tool::Curve( edge, u0, u1 );
6058             gp_Vec du;
6059             curve->D1( u, p, du );
6060             double size2 = du.SquareMagnitude();
6061             if ( du.SquareMagnitude() > tol2 )
6062             {
6063               tangent += du.Divided( Sqrt( size2 )) * id2factor->second;
6064               nbEdges++;
6065             }
6066             break;
6067           }
6068         }
6069       }
6070       if ( nbEdges > 0 )
6071         break;
6072     }
6073     default:
6074     {
6075       for ( int di = -1; di <= 1; di += 2 )
6076       {
6077         size_t j = i + di;
6078         if ( j < pathNodes.size() )
6079         {
6080           gp_Vec dir( point.myPnt, SMESH_NodeXYZ( pathNodes[ j ]));
6081           double size2 = dir.SquareMagnitude();
6082           if ( size2 > tol2 )
6083             tangent += dir.Divided( Sqrt( size2 )) * di;
6084         }
6085       }
6086     }
6087     } // switch ( shapeType )
6088
6089     if ( tangent.SquareMagnitude() < tol2 )
6090       return EXTR_CANT_GET_TANGENT;
6091
6092     point.myTgt = tangent;
6093
6094   } // loop on pathNodes
6095
6096
6097   ExtrusParam nodeMaker( points, theRefPoint, theScales, theMakeGroups );
6098   TTElemOfElemListMap newElemsMap;
6099
6100   ExtrusionSweep( theElements, nodeMaker, newElemsMap );
6101
6102   return EXTR_OK;
6103 }
6104
6105 //=======================================================================
6106 //function : linearAngleVariation
6107 //purpose  : spread values over nbSteps
6108 //=======================================================================
6109
6110 void SMESH_MeshEditor::linearAngleVariation(const int     nbSteps,
6111                                             list<double>& Angles)
6112 {
6113   int nbAngles = Angles.size();
6114   if( nbSteps > nbAngles && nbAngles > 0 )
6115   {
6116     vector<double> theAngles(nbAngles);
6117     theAngles.assign( Angles.begin(), Angles.end() );
6118
6119     list<double> res;
6120     double rAn2St = double( nbAngles ) / double( nbSteps );
6121     double angPrev = 0, angle;
6122     for ( int iSt = 0; iSt < nbSteps; ++iSt )
6123     {
6124       double angCur = rAn2St * ( iSt+1 );
6125       double angCurFloor  = floor( angCur );
6126       double angPrevFloor = floor( angPrev );
6127       if ( angPrevFloor == angCurFloor )
6128         angle = rAn2St * theAngles[ int( angCurFloor ) ];
6129       else {
6130         int iP = int( angPrevFloor );
6131         double angPrevCeil = ceil(angPrev);
6132         angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6133
6134         int iC = int( angCurFloor );
6135         if ( iC < nbAngles )
6136           angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6137
6138         iP = int( angPrevCeil );
6139         while ( iC-- > iP )
6140           angle += theAngles[ iC ];
6141       }
6142       res.push_back(angle);
6143       angPrev = angCur;
6144     }
6145     Angles.swap( res );
6146   }
6147 }
6148
6149 //=======================================================================
6150 //function : linearScaleVariation
6151 //purpose  : spread values over nbSteps 
6152 //=======================================================================
6153
6154 void SMESH_MeshEditor::linearScaleVariation(const int          theNbSteps,
6155                                             std::list<double>& theScales)
6156 {
6157   int nbScales = theScales.size();
6158   std::vector<double> myScales;
6159   myScales.reserve( theNbSteps );
6160   std::list<double>::const_iterator scale = theScales.begin();
6161   double prevScale = 1.0;
6162   for ( int iSc = 1; scale != theScales.end(); ++scale, ++iSc )
6163   {
6164     int      iStep = int( iSc / double( nbScales ) * theNbSteps + 0.5 );
6165     int    stDelta = Max( 1, iStep - myScales.size());
6166     double scDelta = ( *scale - prevScale ) / stDelta;
6167     for ( int iStep = 0; iStep < stDelta; ++iStep )
6168     {
6169       myScales.push_back( prevScale + scDelta );
6170       prevScale = myScales.back();
6171     }
6172     prevScale = *scale;
6173   }
6174   theScales.assign( myScales.begin(), myScales.end() );
6175 }
6176
6177 //================================================================================
6178 /*!
6179  * \brief Move or copy theElements applying theTrsf to their nodes
6180  *  \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6181  *  \param theTrsf - transformation to apply
6182  *  \param theCopy - if true, create translated copies of theElems
6183  *  \param theMakeGroups - if true and theCopy, create translated groups
6184  *  \param theTargetMesh - mesh to copy translated elements into
6185  *  \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6186  */
6187 //================================================================================
6188
6189 SMESH_MeshEditor::PGroupIDs
6190 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6191                              const gp_Trsf&     theTrsf,
6192                              const bool         theCopy,
6193                              const bool         theMakeGroups,
6194                              SMESH_Mesh*        theTargetMesh)
6195 {
6196   ClearLastCreated();
6197   myLastCreatedElems.reserve( theElems.size() );
6198
6199   bool needReverse = false;
6200   string groupPostfix;
6201   switch ( theTrsf.Form() ) {
6202   case gp_PntMirror:
6203     needReverse = true;
6204     groupPostfix = "mirrored";
6205     break;
6206   case gp_Ax1Mirror:
6207     groupPostfix = "mirrored";
6208     break;
6209   case gp_Ax2Mirror:
6210     needReverse = true;
6211     groupPostfix = "mirrored";
6212     break;
6213   case gp_Rotation:
6214     groupPostfix = "rotated";
6215     break;
6216   case gp_Translation:
6217     groupPostfix = "translated";
6218     break;
6219   case gp_Scale:
6220     groupPostfix = "scaled";
6221     break;
6222   case gp_CompoundTrsf: // different scale by axis
6223     groupPostfix = "scaled";
6224     break;
6225   default:
6226     needReverse = false;
6227     groupPostfix = "transformed";
6228   }
6229
6230   SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6231   SMESHDS_Mesh* aMesh    = GetMeshDS();
6232
6233   SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6234   SMESH_MeshEditor* editor = theTargetMesh ? & targetMeshEditor : theCopy ? this : 0;
6235   SMESH_MeshEditor::ElemFeatures elemType;
6236
6237   // map old node to new one
6238   TNodeNodeMap nodeMap;
6239
6240   // elements sharing moved nodes; those of them which have all
6241   // nodes mirrored but are not in theElems are to be reversed
6242   TIDSortedElemSet inverseElemSet;
6243
6244   // source elements for each generated one
6245   SMESH_SequenceOfElemPtr srcElems, srcNodes;
6246
6247   // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6248   TIDSortedElemSet orphanNode;
6249
6250   if ( theElems.empty() ) // transform the whole mesh
6251   {
6252     // add all elements
6253     SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6254     while ( eIt->more() ) theElems.insert( eIt->next() );
6255     // add orphan nodes
6256     SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6257     while ( nIt->more() )
6258     {
6259       const SMDS_MeshNode* node = nIt->next();
6260       if ( node->NbInverseElements() == 0)
6261         orphanNode.insert( node );
6262     }
6263   }
6264
6265   // loop on elements to transform nodes : first orphan nodes then elems
6266   TIDSortedElemSet::iterator itElem;
6267   TIDSortedElemSet *elements[] = { &orphanNode, &theElems };
6268   for (int i=0; i<2; i++)
6269     for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ )
6270     {
6271       const SMDS_MeshElement* elem = *itElem;
6272       if ( !elem )
6273         continue;
6274
6275       // loop on elem nodes
6276       double coord[3];
6277       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6278       while ( itN->more() )
6279       {
6280         const SMDS_MeshNode* node = cast2Node( itN->next() );
6281         // check if a node has been already transformed
6282         pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6283           nodeMap.insert( make_pair ( node, node ));
6284         if ( !n2n_isnew.second )
6285           continue;
6286
6287         node->GetXYZ( coord );
6288         theTrsf.Transforms( coord[0], coord[1], coord[2] );
6289         if ( theTargetMesh ) {
6290           const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6291           n2n_isnew.first->second = newNode;
6292           myLastCreatedNodes.push_back(newNode);
6293           srcNodes.push_back( node );
6294         }
6295         else if ( theCopy ) {
6296           const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6297           n2n_isnew.first->second = newNode;
6298           myLastCreatedNodes.push_back(newNode);
6299           srcNodes.push_back( node );
6300         }
6301         else {
6302           aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6303           // node position on shape becomes invalid
6304           const_cast< SMDS_MeshNode* > ( node )->SetPosition
6305             ( SMDS_SpacePosition::originSpacePosition() );
6306         }
6307
6308         // keep inverse elements
6309         if ( !theCopy && !theTargetMesh && needReverse ) {
6310           SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6311           while ( invElemIt->more() ) {
6312             const SMDS_MeshElement* iel = invElemIt->next();
6313             inverseElemSet.insert( iel );
6314           }
6315         }
6316       }
6317     } // loop on elems in { &orphanNode, &theElems };
6318
6319   // either create new elements or reverse mirrored ones
6320   if ( !theCopy && !needReverse && !theTargetMesh )
6321     return PGroupIDs();
6322
6323   theElems.insert( inverseElemSet.begin(),inverseElemSet.end() );
6324
6325   // Replicate or reverse elements
6326
6327   std::vector<int> iForw;
6328   vector<const SMDS_MeshNode*> nodes;
6329   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6330   {
6331     const SMDS_MeshElement* elem = *itElem;
6332     if ( !elem ) continue;
6333
6334     SMDSAbs_GeometryType geomType = elem->GetGeomType();
6335     size_t               nbNodes  = elem->NbNodes();
6336     if ( geomType == SMDSGeom_NONE ) continue; // node
6337
6338     nodes.resize( nbNodes );
6339
6340     if ( geomType == SMDSGeom_POLYHEDRA )  // ------------------ polyhedral volume
6341     {
6342       const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( elem );
6343       if ( !aPolyedre )
6344         continue;
6345       nodes.clear();
6346       bool allTransformed = true;
6347       int nbFaces = aPolyedre->NbFaces();
6348       for (int iface = 1; iface <= nbFaces && allTransformed; iface++)
6349       {
6350         int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6351         for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++)
6352         {
6353           const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6354           TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6355           if ( nodeMapIt == nodeMap.end() )
6356             allTransformed = false; // not all nodes transformed
6357           else
6358             nodes.push_back((*nodeMapIt).second);
6359         }
6360         if ( needReverse && allTransformed )
6361           std::reverse( nodes.end() - nbFaceNodes, nodes.end() );
6362       }
6363       if ( !allTransformed )
6364         continue; // not all nodes transformed
6365     }
6366     else // ----------------------- the rest element types
6367     {
6368       while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6369       const vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType(), nbNodes );
6370       const vector<int>&    i = needReverse ? iRev : iForw;
6371
6372       // find transformed nodes
6373       size_t iNode = 0;
6374       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6375       while ( itN->more() ) {
6376         const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
6377         TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6378         if ( nodeMapIt == nodeMap.end() )
6379           break; // not all nodes transformed
6380         nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
6381       }
6382       if ( iNode != nbNodes )
6383         continue; // not all nodes transformed
6384     }
6385
6386     if ( editor ) {
6387       // copy in this or a new mesh
6388       if ( editor->AddElement( nodes, elemType.Init( elem, /*basicOnly=*/false )))
6389         srcElems.push_back( elem );
6390     }
6391     else {
6392       // reverse element as it was reversed by transformation
6393       if ( nbNodes > 2 )
6394         aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
6395     }
6396
6397   } // loop on elements
6398
6399   if ( editor && editor != this )
6400     myLastCreatedElems.swap( editor->myLastCreatedElems );
6401
6402   PGroupIDs newGroupIDs;
6403
6404   if ( ( theMakeGroups && theCopy ) ||
6405        ( theMakeGroups && theTargetMesh ) )
6406     newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh, false );
6407
6408   return newGroupIDs;
6409 }
6410
6411 //================================================================================
6412 /*!
6413  * \brief Make an offset mesh from a source 2D mesh
6414  *  \param [in] theElements - source faces
6415  *  \param [in] theValue - offset value
6416  *  \param [out] theTgtMesh - a mesh to add offset elements to
6417  *  \param [in] theMakeGroups - to generate groups
6418  *  \return PGroupIDs - IDs of created groups. NULL means failure
6419  */
6420 //================================================================================
6421
6422 SMESH_MeshEditor::PGroupIDs SMESH_MeshEditor::Offset( TIDSortedElemSet & theElements,
6423                                                       const double       theValue,
6424                                                       SMESH_Mesh*        theTgtMesh,
6425                                                       const bool         theMakeGroups,
6426                                                       const bool         theCopyElements,
6427                                                       const bool         theFixSelfIntersection)
6428 {
6429   SMESHDS_Mesh*    meshDS = GetMeshDS();
6430   SMESHDS_Mesh* tgtMeshDS = theTgtMesh->GetMeshDS();
6431   SMESH_MeshEditor tgtEditor( theTgtMesh );
6432
6433   SMDS_ElemIteratorPtr eIt;
6434   if ( theElements.empty() ) eIt = meshDS->elementsIterator( SMDSAbs_Face );
6435   else                       eIt = SMESHUtils::elemSetIterator( theElements );
6436
6437   SMESH_MeshAlgos::TElemIntPairVec new2OldFaces;
6438   SMESH_MeshAlgos::TNodeIntPairVec new2OldNodes;
6439   std::unique_ptr< SMDS_Mesh > offsetMesh
6440     ( SMESH_MeshAlgos::MakeOffset( eIt, *meshDS, theValue,
6441                                    theFixSelfIntersection,
6442                                    new2OldFaces, new2OldNodes ));
6443   if ( offsetMesh->NbElements() == 0 )
6444     return PGroupIDs(); // MakeOffset() failed
6445
6446
6447   if ( theTgtMesh == myMesh && !theCopyElements )
6448   {
6449     // clear the source elements
6450     if ( theElements.empty() ) eIt = meshDS->elementsIterator( SMDSAbs_Face );
6451     else                       eIt = SMESHUtils::elemSetIterator( theElements );
6452     while ( eIt->more() )
6453       meshDS->RemoveFreeElement( eIt->next(), 0 );
6454   }
6455
6456   // offsetMesh->Modified();
6457   // offsetMesh->CompactMesh(); // make IDs start from 1
6458
6459   // source elements for each generated one
6460   SMESH_SequenceOfElemPtr srcElems, srcNodes;
6461   srcElems.reserve( new2OldFaces.size() );
6462   srcNodes.reserve( new2OldNodes.size() );
6463
6464   ClearLastCreated();
6465   myLastCreatedElems.reserve( new2OldFaces.size() );
6466   myLastCreatedNodes.reserve( new2OldNodes.size() );
6467
6468   // copy offsetMesh to theTgtMesh
6469
6470   int idShift = meshDS->MaxNodeID();
6471   for ( size_t i = 0; i < new2OldNodes.size(); ++i )
6472     if ( const SMDS_MeshNode* n = new2OldNodes[ i ].first )
6473     {
6474 #ifndef _DEBUG_
6475       if ( n->NbInverseElements() > 0 )
6476 #endif
6477       {
6478         const SMDS_MeshNode* n2 =
6479           tgtMeshDS->AddNodeWithID( n->X(), n->Y(), n->Z(), idShift + n->GetID() );
6480         myLastCreatedNodes.push_back( n2 );
6481         srcNodes.push_back( meshDS->FindNode( new2OldNodes[ i ].second ));
6482       }
6483     }
6484
6485   ElemFeatures elemType;
6486   for ( size_t i = 0; i < new2OldFaces.size(); ++i )
6487     if ( const SMDS_MeshElement* f = new2OldFaces[ i ].first )
6488     {
6489       elemType.Init( f );
6490       elemType.myNodes.clear();
6491       for ( SMDS_NodeIteratorPtr nIt = f->nodeIterator(); nIt->more(); )
6492       {
6493         const SMDS_MeshNode* n2 = nIt->next();
6494         elemType.myNodes.push_back( tgtMeshDS->FindNode( idShift + n2->GetID() ));
6495       }
6496       tgtEditor.AddElement( elemType.myNodes, elemType );
6497       srcElems.push_back( meshDS->FindElement( new2OldFaces[ i ].second ));
6498     }
6499
6500   myLastCreatedElems.swap( tgtEditor.myLastCreatedElems );
6501
6502   PGroupIDs newGroupIDs;
6503   if ( theMakeGroups )
6504     newGroupIDs = generateGroups( srcNodes, srcElems, "offset", theTgtMesh, false );
6505   else
6506     newGroupIDs.reset( new std::list< int > );
6507
6508   return newGroupIDs;
6509 }
6510
6511 //=======================================================================
6512 /*!
6513  * \brief Create groups of elements made during transformation
6514  *  \param nodeGens - nodes making corresponding myLastCreatedNodes
6515  *  \param elemGens - elements making corresponding myLastCreatedElems
6516  *  \param postfix - to push_back to names of new groups
6517  *  \param targetMesh - mesh to create groups in
6518  *  \param topPresent - is there are "top" elements that are created by sweeping
6519  */
6520 //=======================================================================
6521
6522 SMESH_MeshEditor::PGroupIDs
6523 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
6524                                  const SMESH_SequenceOfElemPtr& elemGens,
6525                                  const std::string&             postfix,
6526                                  SMESH_Mesh*                    targetMesh,
6527                                  const bool                     topPresent)
6528 {
6529   PGroupIDs newGroupIDs( new list<int> );
6530   SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6531
6532   // Sort existing groups by types and collect their names
6533
6534   // containers to store an old group and generated new ones;
6535   // 1st new group is for result elems of different type than a source one;
6536   // 2nd new group is for same type result elems ("top" group at extrusion)
6537   using boost::tuple;
6538   using boost::make_tuple;
6539   typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
6540   vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
6541   vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
6542   // group names
6543   set< string > groupNames;
6544
6545   SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
6546   if ( !groupIt->more() ) return newGroupIDs;
6547
6548   int newGroupID = mesh->GetGroupIds().back()+1;
6549   while ( groupIt->more() )
6550   {
6551     SMESH_Group * group = groupIt->next();
6552     if ( !group ) continue;
6553     SMESHDS_GroupBase* groupDS = group->GetGroupDS();
6554     if ( !groupDS || groupDS->IsEmpty() ) continue;
6555     groupNames.insert    ( group->GetName() );
6556     groupDS->SetStoreName( group->GetName() );
6557     const SMDSAbs_ElementType type = groupDS->GetType();
6558     SMESHDS_Group* newGroup    = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6559     SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6560     groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
6561     orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
6562   }
6563
6564   // Loop on nodes and elements to add them in new groups
6565
6566   vector< const SMDS_MeshElement* > resultElems;
6567   for ( int isNodes = 0; isNodes < 2; ++isNodes )
6568   {
6569     const SMESH_SequenceOfElemPtr& gens  = isNodes ? nodeGens : elemGens;
6570     const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
6571     if ( gens.size() != elems.size() )
6572       throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
6573
6574     // loop on created elements
6575     for (size_t iElem = 0; iElem < elems.size(); ++iElem )
6576     {
6577       const SMDS_MeshElement* sourceElem = gens[ iElem ];
6578       if ( !sourceElem ) {
6579         MESSAGE("generateGroups(): NULL source element");
6580         continue;
6581       }
6582       list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
6583       if ( groupsOldNew.empty() ) { // no groups of this type at all
6584         while ( iElem+1 < gens.size() && gens[ iElem+1 ] == sourceElem )
6585           ++iElem; // skip all elements made by sourceElem
6586         continue;
6587       }
6588       // collect all elements made by the iElem-th sourceElem
6589       resultElems.clear();
6590       if ( const SMDS_MeshElement* resElem = elems[ iElem ])
6591         if ( resElem != sourceElem )
6592           resultElems.push_back( resElem );
6593       while ( iElem+1 < gens.size() && gens[ iElem+1 ] == sourceElem )
6594         if ( const SMDS_MeshElement* resElem = elems[ ++iElem ])
6595           if ( resElem != sourceElem )
6596             resultElems.push_back( resElem );
6597
6598       const SMDS_MeshElement* topElem = 0;
6599       if ( isNodes ) // there must be a top element
6600       {
6601         topElem = resultElems.back();
6602         resultElems.pop_back();
6603       }
6604       else
6605       {
6606         vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
6607         for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
6608           if ( (*resElemIt)->GetType() == sourceElem->GetType() )
6609           {
6610             topElem = *resElemIt;
6611             *resElemIt = 0; // erase *resElemIt
6612             break;
6613           }
6614       }
6615       // add resultElems to groups originted from ones the sourceElem belongs to
6616       list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
6617       for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
6618       {
6619         SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
6620         if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
6621         {
6622           // fill in a new group
6623           SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
6624           vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
6625           for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
6626             if ( *resElemIt )
6627               newGroup.Add( *resElemIt );
6628
6629           // fill a "top" group
6630           if ( topElem )
6631           {
6632             SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
6633             newTopGroup.Add( topElem );
6634           }
6635         }
6636       }
6637     } // loop on created elements
6638   }// loop on nodes and elements
6639
6640   // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
6641
6642   list<int> topGrouIds;
6643   for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
6644   {
6645     SMESHDS_GroupBase* oldGroupDS =   orderedOldNewGroups[i]->get<0>();
6646     SMESHDS_Group*   newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
6647                                       orderedOldNewGroups[i]->get<2>() };
6648     for ( int is2nd = 0; is2nd < 2; ++is2nd )
6649     {
6650       SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
6651       if ( newGroupDS->IsEmpty() )
6652       {
6653         mesh->GetMeshDS()->RemoveGroup( newGroupDS );
6654       }
6655       else
6656       {
6657         // set group type
6658         newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
6659
6660         // make a name
6661         const bool isTop = ( topPresent &&
6662                              newGroupDS->GetType() == oldGroupDS->GetType() &&
6663                              is2nd );
6664
6665         string name = oldGroupDS->GetStoreName();
6666         { // remove trailing whitespaces (issue 22599)
6667           size_t size = name.size();
6668           while ( size > 1 && isspace( name[ size-1 ]))
6669             --size;
6670           if ( size != name.size() )
6671           {
6672             name.resize( size );
6673             oldGroupDS->SetStoreName( name.c_str() );
6674           }
6675         }
6676         if ( !targetMesh ) {
6677           string suffix = ( isTop ? "top": postfix.c_str() );
6678           name += "_";
6679           name += suffix;
6680           int nb = 1;
6681           while ( !groupNames.insert( name ).second ) // name exists
6682             name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
6683         }
6684         else if ( isTop ) {
6685           name += "_top";
6686         }
6687         newGroupDS->SetStoreName( name.c_str() );
6688
6689         // make a SMESH_Groups
6690         mesh->AddGroup( newGroupDS );
6691         if ( isTop )
6692           topGrouIds.push_back( newGroupDS->GetID() );
6693         else
6694           newGroupIDs->push_back( newGroupDS->GetID() );
6695       }
6696     }
6697   }
6698   newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
6699
6700   return newGroupIDs;
6701 }
6702
6703 //================================================================================
6704 /*!
6705  *  * \brief Return list of group of nodes close to each other within theTolerance
6706  *  *        Search among theNodes or in the whole mesh if theNodes is empty using
6707  *  *        an Octree algorithm
6708  *  \param [in,out] theNodes - the nodes to treat
6709  *  \param [in]     theTolerance - the tolerance
6710  *  \param [out]    theGroupsOfNodes - the result groups of coincident nodes
6711  *  \param [in]     theSeparateCornersAndMedium - if \c true, in quadratic mesh puts 
6712  *         corner and medium nodes in separate groups
6713  */
6714 //================================================================================
6715
6716 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet &   theNodes,
6717                                             const double         theTolerance,
6718                                             TListOfListOfNodes & theGroupsOfNodes,
6719                                             bool                 theSeparateCornersAndMedium)
6720 {
6721   ClearLastCreated();
6722
6723   if ( myMesh->NbEdges  ( ORDER_QUADRATIC ) +
6724        myMesh->NbFaces  ( ORDER_QUADRATIC ) +
6725        myMesh->NbVolumes( ORDER_QUADRATIC ) == 0 )
6726     theSeparateCornersAndMedium = false;
6727
6728   TIDSortedNodeSet& corners = theNodes;
6729   TIDSortedNodeSet  medium;
6730
6731   if ( theNodes.empty() ) // get all nodes in the mesh
6732   {
6733     TIDSortedNodeSet* nodes[2] = { &corners, &medium };
6734     SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator();
6735     if ( theSeparateCornersAndMedium )
6736       while ( nIt->more() )
6737       {
6738         const SMDS_MeshNode* n = nIt->next();
6739         TIDSortedNodeSet* & nodeSet = nodes[ SMESH_MesherHelper::IsMedium( n )];
6740         nodeSet->insert( nodeSet->end(), n );
6741       }
6742     else
6743       while ( nIt->more() )
6744         theNodes.insert( theNodes.end(), nIt->next() );
6745   }
6746   else if ( theSeparateCornersAndMedium ) // separate corners from medium nodes
6747   {
6748     TIDSortedNodeSet::iterator nIt = corners.begin();
6749     while ( nIt != corners.end() )
6750       if ( SMESH_MesherHelper::IsMedium( *nIt ))
6751       {
6752         medium.insert( medium.end(), *nIt );
6753         corners.erase( nIt++ );
6754       }
6755       else
6756       {
6757         ++nIt;
6758       }
6759   }
6760
6761   if ( !corners.empty() )
6762     SMESH_OctreeNode::FindCoincidentNodes ( corners, &theGroupsOfNodes, theTolerance );
6763   if ( !medium.empty() )
6764     SMESH_OctreeNode::FindCoincidentNodes ( medium, &theGroupsOfNodes, theTolerance );
6765 }
6766
6767 //=======================================================================
6768 //function : SimplifyFace
6769 //purpose  : split a chain of nodes into several closed chains
6770 //=======================================================================
6771
6772 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
6773                                     vector<const SMDS_MeshNode *>&       poly_nodes,
6774                                     vector<int>&                         quantities) const
6775 {
6776   int nbNodes = faceNodes.size();
6777   while ( faceNodes[ 0 ] == faceNodes[ nbNodes-1 ] && nbNodes > 2 )
6778     --nbNodes;
6779   if ( nbNodes < 3 )
6780     return 0;
6781   size_t prevNbQuant = quantities.size();
6782
6783   vector< const SMDS_MeshNode* > simpleNodes; simpleNodes.reserve( nbNodes );
6784   map< const SMDS_MeshNode*, int > nodeIndices; // indices within simpleNodes
6785   map< const SMDS_MeshNode*, int >::iterator nInd;
6786
6787   nodeIndices.insert( make_pair( faceNodes[0], 0 ));
6788   simpleNodes.push_back( faceNodes[0] );
6789   for ( int iCur = 1; iCur < nbNodes; iCur++ )
6790   {
6791     if ( faceNodes[ iCur ] != simpleNodes.back() )
6792     {
6793       int index = simpleNodes.size();
6794       nInd = nodeIndices.insert( make_pair( faceNodes[ iCur ], index )).first;
6795       int prevIndex = nInd->second;
6796       if ( prevIndex < index )
6797       {
6798         // a sub-loop found
6799         int loopLen = index - prevIndex;
6800         if ( loopLen > 2 )
6801         {
6802           // store the sub-loop
6803           quantities.push_back( loopLen );
6804           for ( int i = prevIndex; i < index; i++ )
6805             poly_nodes.push_back( simpleNodes[ i ]);
6806         }
6807         simpleNodes.resize( prevIndex+1 );
6808       }
6809       else
6810       {
6811         simpleNodes.push_back( faceNodes[ iCur ]);
6812       }
6813     }
6814   }
6815
6816   if ( simpleNodes.size() > 2 )
6817   {
6818     quantities.push_back( simpleNodes.size() );
6819     poly_nodes.insert ( poly_nodes.end(), simpleNodes.begin(), simpleNodes.end() );
6820   }
6821
6822   return quantities.size() - prevNbQuant;
6823 }
6824
6825 //=======================================================================
6826 //function : MergeNodes
6827 //purpose  : In each group, the cdr of nodes are substituted by the first one
6828 //           in all elements.
6829 //=======================================================================
6830
6831 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes,
6832                                    const bool           theAvoidMakingHoles)
6833 {
6834   ClearLastCreated();
6835
6836   SMESHDS_Mesh* mesh = GetMeshDS();
6837
6838   TNodeNodeMap nodeNodeMap; // node to replace - new node
6839   set<const SMDS_MeshElement*> elems; // all elements with changed nodes
6840   list< int > rmElemIds, rmNodeIds;
6841   vector< ElemFeatures > newElemDefs;
6842
6843   // Fill nodeNodeMap and elems
6844
6845   TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
6846   for ( ; grIt != theGroupsOfNodes.end(); grIt++ )
6847   {
6848     list<const SMDS_MeshNode*>& nodes = *grIt;
6849     list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
6850     const SMDS_MeshNode* nToKeep = *nIt;
6851     for ( ++nIt; nIt != nodes.end(); nIt++ )
6852     {
6853       const SMDS_MeshNode* nToRemove = *nIt;
6854       nodeNodeMap.insert( make_pair( nToRemove, nToKeep ));
6855       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
6856       while ( invElemIt->more() ) {
6857         const SMDS_MeshElement* elem = invElemIt->next();
6858         elems.insert(elem);
6859       }
6860     }
6861   }
6862
6863   // Apply recursive replacements (BUG 0020185)
6864   TNodeNodeMap::iterator nnIt = nodeNodeMap.begin();
6865   for ( ; nnIt != nodeNodeMap.end(); ++nnIt )
6866   {
6867     const SMDS_MeshNode* nToKeep = nnIt->second;
6868     TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( nToKeep );
6869     while ( nnIt_i != nodeNodeMap.end() && nnIt_i->second != nnIt->second )
6870     {
6871       nToKeep = nnIt_i->second;
6872       nnIt->second = nToKeep;
6873       nnIt_i = nodeNodeMap.find( nToKeep );
6874     }
6875   }
6876
6877   if ( theAvoidMakingHoles )
6878   {
6879     // find elements whose topology changes
6880
6881     vector<const SMDS_MeshElement*> pbElems;
6882     set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
6883     for ( ; eIt != elems.end(); ++eIt )
6884     {
6885       const SMDS_MeshElement* elem = *eIt;
6886       SMDS_ElemIteratorPtr     itN = elem->nodesIterator();
6887       while ( itN->more() )
6888       {
6889         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
6890         TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
6891         if ( nnIt != nodeNodeMap.end() && elem->GetNodeIndex( nnIt->second ) >= 0 )
6892         {
6893           // several nodes of elem stick
6894           pbElems.push_back( elem );
6895           break;
6896         }
6897       }
6898     }
6899     // exclude from merge nodes causing spoiling element
6900     for ( size_t iLoop = 0; iLoop < pbElems.size(); ++iLoop ) // avoid infinite cycle
6901     {
6902       bool nodesExcluded = false;
6903       for ( size_t i = 0; i < pbElems.size(); ++i )
6904       {
6905         size_t prevNbMergeNodes = nodeNodeMap.size();
6906         if ( !applyMerge( pbElems[i], newElemDefs, nodeNodeMap, /*noHoles=*/true ) &&
6907              prevNbMergeNodes < nodeNodeMap.size() )
6908           nodesExcluded = true;
6909       }
6910       if ( !nodesExcluded )
6911         break;
6912     }
6913   }
6914
6915   for ( nnIt = nodeNodeMap.begin(); nnIt != nodeNodeMap.end(); ++nnIt )
6916   {
6917     const SMDS_MeshNode* nToRemove = nnIt->first;
6918     const SMDS_MeshNode* nToKeep   = nnIt->second;
6919     if ( nToRemove != nToKeep )
6920     {
6921       rmNodeIds.push_back( nToRemove->GetID() );
6922       AddToSameGroups( nToKeep, nToRemove, mesh );
6923       // set _alwaysComputed to a sub-mesh of VERTEX to enable further mesh computing
6924       // w/o creating node in place of merged ones.
6925       SMDS_PositionPtr pos = nToRemove->GetPosition();
6926       if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
6927         if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
6928           sm->SetIsAlwaysComputed( true );
6929     }
6930   }
6931
6932   // Change element nodes or remove an element
6933
6934   set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
6935   for ( ; eIt != elems.end(); eIt++ )
6936   {
6937     const SMDS_MeshElement* elem = *eIt;
6938     SMESHDS_SubMesh*          sm = mesh->MeshElements( elem->getshapeId() );
6939
6940     bool keepElem = applyMerge( elem, newElemDefs, nodeNodeMap, /*noHoles=*/false );
6941     if ( !keepElem )
6942       rmElemIds.push_back( elem->GetID() );
6943
6944     for ( size_t i = 0; i < newElemDefs.size(); ++i )
6945     {
6946       if ( i > 0 || !mesh->ChangeElementNodes( elem,
6947                                                & newElemDefs[i].myNodes[0],
6948                                                newElemDefs[i].myNodes.size() ))
6949       {
6950         if ( i == 0 )
6951         {
6952           newElemDefs[i].SetID( elem->GetID() );
6953           mesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
6954           if ( !keepElem ) rmElemIds.pop_back();
6955         }
6956         else
6957         {
6958           newElemDefs[i].SetID( -1 );
6959         }
6960         SMDS_MeshElement* newElem = this->AddElement( newElemDefs[i].myNodes, newElemDefs[i] );
6961         if ( sm && newElem )
6962           sm->AddElement( newElem );
6963         if ( elem != newElem )
6964           ReplaceElemInGroups( elem, newElem, mesh );
6965       }
6966     }
6967   }
6968
6969   // Remove bad elements, then equal nodes (order important)
6970   Remove( rmElemIds, /*isNodes=*/false );
6971   Remove( rmNodeIds, /*isNodes=*/true );
6972
6973   return;
6974 }
6975
6976 //=======================================================================
6977 //function : applyMerge
6978 //purpose  : Compute new connectivity of an element after merging nodes
6979 //  \param [in] elems - the element
6980 //  \param [out] newElemDefs - definition(s) of result element(s)
6981 //  \param [inout] nodeNodeMap - nodes to merge
6982 //  \param [in] avoidMakingHoles - if true and and the element becomes invalid
6983 //              after merging (but not degenerated), removes nodes causing
6984 //              the invalidity from \a nodeNodeMap.
6985 //  \return bool - true if the element should be removed
6986 //=======================================================================
6987
6988 bool SMESH_MeshEditor::applyMerge( const SMDS_MeshElement* elem,
6989                                    vector< ElemFeatures >& newElemDefs,
6990                                    TNodeNodeMap&           nodeNodeMap,
6991                                    const bool              avoidMakingHoles )
6992 {
6993   bool toRemove = false; // to remove elem
6994   int nbResElems = 1;    // nb new elements
6995
6996   newElemDefs.resize(nbResElems);
6997   newElemDefs[0].Init( elem );
6998   newElemDefs[0].myNodes.clear();
6999
7000   set<const SMDS_MeshNode*> nodeSet;
7001   vector< const SMDS_MeshNode*>   curNodes;
7002   vector< const SMDS_MeshNode*> & uniqueNodes = newElemDefs[0].myNodes;
7003   vector<int> iRepl;
7004
7005   const        int  nbNodes = elem->NbNodes();
7006   SMDSAbs_EntityType entity = elem->GetEntityType();
7007
7008   curNodes.resize( nbNodes );
7009   uniqueNodes.resize( nbNodes );
7010   iRepl.resize( nbNodes );
7011   int iUnique = 0, iCur = 0, nbRepl = 0;
7012
7013   // Get new seq of nodes
7014
7015   SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7016   while ( itN->more() )
7017   {
7018     const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7019
7020     TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7021     if ( nnIt != nodeNodeMap.end() ) {
7022       n = (*nnIt).second;
7023     }
7024     curNodes[ iCur ] = n;
7025     bool isUnique = nodeSet.insert( n ).second;
7026     if ( isUnique )
7027       uniqueNodes[ iUnique++ ] = n;
7028     else
7029       iRepl[ nbRepl++ ] = iCur;
7030     iCur++;
7031   }
7032
7033   // Analyse element topology after replacement
7034
7035   int nbUniqueNodes = nodeSet.size();
7036   if ( nbNodes != nbUniqueNodes ) // some nodes stick
7037   {
7038     toRemove = true;
7039     nbResElems = 0;
7040
7041     if ( newElemDefs[0].myIsQuad && newElemDefs[0].myType == SMDSAbs_Face && nbNodes > 6 )
7042     {
7043       // if corner nodes stick, remove medium nodes between them from uniqueNodes
7044       int nbCorners = nbNodes / 2;
7045       for ( int iCur = 0; iCur < nbCorners; ++iCur )
7046       {
7047         int iNext = ( iCur + 1 ) % nbCorners;
7048         if ( curNodes[ iCur ] == curNodes[ iNext ] ) // corners stick
7049         {
7050           int iMedium = iCur + nbCorners;
7051           vector< const SMDS_MeshNode* >::iterator i =
7052             std::find( uniqueNodes.begin() + nbCorners - nbRepl,
7053                        uniqueNodes.end(),
7054                        curNodes[ iMedium ]);
7055           if ( i != uniqueNodes.end() )
7056           {
7057             --nbUniqueNodes;
7058             for ( ; i+1 != uniqueNodes.end(); ++i )
7059               *i = *(i+1);
7060           }
7061         }
7062       }
7063     }
7064
7065     switch ( entity )
7066     {
7067     case SMDSEntity_Polygon:
7068     case SMDSEntity_Quad_Polygon: // Polygon
7069     {
7070       ElemFeatures* elemType = & newElemDefs[0];
7071       const bool isQuad = elemType->myIsQuad;
7072       if ( isQuad )
7073         SMDS_MeshCell::applyInterlace // interlace medium and corner nodes
7074           ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon, nbNodes ), curNodes );
7075
7076       // a polygon can divide into several elements
7077       vector<const SMDS_MeshNode *> polygons_nodes;
7078       vector<int> quantities;
7079       nbResElems = SimplifyFace( curNodes, polygons_nodes, quantities );
7080       newElemDefs.resize( nbResElems );
7081       for ( int inode = 0, iface = 0; iface < nbResElems; iface++ )
7082       {
7083         ElemFeatures* elemType = & newElemDefs[iface];
7084         if ( iface ) elemType->Init( elem );
7085
7086         vector<const SMDS_MeshNode *>& face_nodes = elemType->myNodes;
7087         int nbNewNodes = quantities[iface];
7088         face_nodes.assign( polygons_nodes.begin() + inode,
7089                            polygons_nodes.begin() + inode + nbNewNodes );
7090         inode += nbNewNodes;
7091         if ( isQuad ) // check if a result elem is a valid quadratic polygon
7092         {
7093           bool isValid = ( nbNewNodes % 2 == 0 );
7094           for ( int i = 0; i < nbNewNodes && isValid; ++i )
7095             isValid = ( elem->IsMediumNode( face_nodes[i]) == bool( i % 2 ));
7096           elemType->SetQuad( isValid );
7097           if ( isValid ) // put medium nodes after corners
7098             SMDS_MeshCell::applyInterlaceRev
7099               ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon,
7100                                                     nbNewNodes ), face_nodes );
7101         }
7102         elemType->SetPoly(( nbNewNodes / ( elemType->myIsQuad + 1 ) > 4 ));
7103       }
7104       nbUniqueNodes = newElemDefs[0].myNodes.size();
7105       break;
7106     } // Polygon
7107
7108     case SMDSEntity_Polyhedra: // Polyhedral volume
7109     {
7110       if ( nbUniqueNodes >= 4 )
7111       {
7112         // each face has to be analyzed in order to check volume validity
7113         if ( const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( elem ))
7114         {
7115           int nbFaces = aPolyedre->NbFaces();
7116
7117           vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7118           vector<int>                  & quantities = newElemDefs[0].myPolyhedQuantities;
7119           vector<const SMDS_MeshNode *>  faceNodes;
7120           poly_nodes.clear();
7121           quantities.clear();
7122
7123           for (int iface = 1; iface <= nbFaces; iface++)
7124           {
7125             int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7126             faceNodes.resize( nbFaceNodes );
7127             for (int inode = 1; inode <= nbFaceNodes; inode++)
7128             {
7129               const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7130               TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7131               if ( nnIt != nodeNodeMap.end() ) // faceNode sticks
7132                 faceNode = (*nnIt).second;
7133               faceNodes[inode - 1] = faceNode;
7134             }
7135             SimplifyFace(faceNodes, poly_nodes, quantities);
7136           }
7137
7138           if ( quantities.size() > 3 )
7139           {
7140             // TODO: remove coincident faces
7141             nbResElems = 1;
7142             nbUniqueNodes = newElemDefs[0].myNodes.size();
7143           }
7144         }
7145       }
7146     }
7147     break;
7148
7149     // Regular elements
7150     // TODO not all the possible cases are solved. Find something more generic?
7151     case SMDSEntity_Edge: //////// EDGE
7152     case SMDSEntity_Triangle: //// TRIANGLE
7153     case SMDSEntity_Quad_Triangle:
7154     case SMDSEntity_Tetra:
7155     case SMDSEntity_Quad_Tetra: // TETRAHEDRON
7156     {
7157       break;
7158     }
7159     case SMDSEntity_Quad_Edge:
7160     {
7161       break;
7162     }
7163     case SMDSEntity_Quadrangle: //////////////////////////////////// QUADRANGLE
7164     {
7165       if ( nbUniqueNodes < 3 )
7166         toRemove = true;
7167       else if ( nbRepl == 1 && curNodes[ iRepl[0]] == curNodes[( iRepl[0]+2 )%4 ])
7168         toRemove = true; // opposite nodes stick
7169       else
7170         toRemove = false;
7171       break;
7172     }
7173     case SMDSEntity_Quad_Quadrangle: // Quadratic QUADRANGLE
7174     {
7175       //   1    5    2
7176       //    +---+---+
7177       //    |       |
7178       //   4+       +6
7179       //    |       |
7180       //    +---+---+
7181       //   0    7    3
7182       if ( nbUniqueNodes == 6 &&
7183            iRepl[0] < 4       &&
7184            ( nbRepl == 1 || iRepl[1] >= 4 ))
7185       {
7186         toRemove = false;
7187       }
7188       break;
7189     }
7190     case SMDSEntity_BiQuad_Quadrangle: // Bi-Quadratic QUADRANGLE
7191     {
7192       //   1    5    2
7193       //    +---+---+
7194       //    |       |
7195       //   4+  8+   +6
7196       //    |       |
7197       //    +---+---+
7198       //   0    7    3
7199       if ( nbUniqueNodes == 7 &&
7200            iRepl[0] < 4       &&
7201            ( nbRepl == 1 || iRepl[1] != 8 ))
7202       {
7203         toRemove = false;
7204       }
7205       break;
7206     }
7207     case SMDSEntity_Penta: ///////////////////////////////////// PENTAHEDRON
7208     {
7209       if ( nbUniqueNodes == 4 ) {
7210         // ---------------------------------> tetrahedron
7211         if ( curNodes[3] == curNodes[4] &&
7212              curNodes[3] == curNodes[5] ) {
7213           // top nodes stick
7214           toRemove = false;
7215         }
7216         else if ( curNodes[0] == curNodes[1] &&
7217                   curNodes[0] == curNodes[2] ) {
7218           // bottom nodes stick: set a top before
7219           uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7220           uniqueNodes[ 0 ] = curNodes [ 5 ];
7221           uniqueNodes[ 1 ] = curNodes [ 4 ];
7222           uniqueNodes[ 2 ] = curNodes [ 3 ];
7223           toRemove = false;
7224         }
7225         else if (( curNodes[0] == curNodes[3] ) +
7226                  ( curNodes[1] == curNodes[4] ) +
7227                  ( curNodes[2] == curNodes[5] ) == 2 ) {
7228           // a lateral face turns into a line
7229           toRemove = false;
7230         }
7231       }
7232       else if ( nbUniqueNodes == 5 ) {
7233         // PENTAHEDRON --------------------> pyramid
7234         if ( curNodes[0] == curNodes[3] )
7235         {
7236           uniqueNodes[ 0 ] = curNodes[ 1 ];
7237           uniqueNodes[ 1 ] = curNodes[ 4 ];
7238           uniqueNodes[ 2 ] = curNodes[ 5 ];
7239           uniqueNodes[ 3 ] = curNodes[ 2 ];
7240           uniqueNodes[ 4 ] = curNodes[ 0 ];
7241           toRemove = false;
7242         }
7243         if ( curNodes[1] == curNodes[4] )
7244         {
7245           uniqueNodes[ 0 ] = curNodes[ 0 ];
7246           uniqueNodes[ 1 ] = curNodes[ 2 ];
7247           uniqueNodes[ 2 ] = curNodes[ 5 ];
7248           uniqueNodes[ 3 ] = curNodes[ 3 ];
7249           uniqueNodes[ 4 ] = curNodes[ 1 ];
7250           toRemove = false;
7251         }
7252         if ( curNodes[2] == curNodes[5] )
7253         {
7254           uniqueNodes[ 0 ] = curNodes[ 0 ];
7255           uniqueNodes[ 1 ] = curNodes[ 3 ];
7256           uniqueNodes[ 2 ] = curNodes[ 4 ];
7257           uniqueNodes[ 3 ] = curNodes[ 1 ];
7258           uniqueNodes[ 4 ] = curNodes[ 2 ];
7259           toRemove = false;
7260         }
7261       }
7262       break;
7263     }
7264     case SMDSEntity_Hexa:
7265     {
7266       //////////////////////////////////// HEXAHEDRON
7267       SMDS_VolumeTool hexa (elem);
7268       hexa.SetExternalNormal();
7269       if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7270         //////////////////////// HEX ---> tetrahedron
7271         for ( int iFace = 0; iFace < 6; iFace++ ) {
7272           const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7273           if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7274               curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7275               curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7276             // one face turns into a point ...
7277             int  pickInd = ind[ 0 ];
7278             int iOppFace = hexa.GetOppFaceIndex( iFace );
7279             ind = hexa.GetFaceNodesIndices( iOppFace );
7280             int nbStick = 0;
7281             uniqueNodes.clear();
7282             for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7283               if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7284                 nbStick++;
7285               else
7286                 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7287             }
7288             if ( nbStick == 1 ) {
7289               // ... and the opposite one - into a triangle.
7290               // set a top node
7291               uniqueNodes.push_back( curNodes[ pickInd ]);
7292               toRemove = false;
7293             }
7294             break;
7295           }
7296         }
7297       }
7298       else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7299         //////////////////////// HEX ---> prism
7300         int nbTria = 0, iTria[3];
7301         const int *ind; // indices of face nodes
7302         // look for triangular faces
7303         for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7304           ind = hexa.GetFaceNodesIndices( iFace );
7305           TIDSortedNodeSet faceNodes;
7306           for ( iCur = 0; iCur < 4; iCur++ )
7307             faceNodes.insert( curNodes[ind[iCur]] );
7308           if ( faceNodes.size() == 3 )
7309             iTria[ nbTria++ ] = iFace;
7310         }
7311         // check if triangles are opposite
7312         if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7313         {
7314           // set nodes of the bottom triangle
7315           ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7316           vector<int> indB;
7317           for ( iCur = 0; iCur < 4; iCur++ )
7318             if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7319               indB.push_back( ind[iCur] );
7320           if ( !hexa.IsForward() )
7321             std::swap( indB[0], indB[2] );
7322           for ( iCur = 0; iCur < 3; iCur++ )
7323             uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7324           // set nodes of the top triangle
7325           const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7326           for ( iCur = 0; iCur < 3; ++iCur )
7327             for ( int j = 0; j < 4; ++j )
7328               if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7329               {
7330                 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7331                 break;
7332               }
7333           toRemove = false;
7334           break;
7335         }
7336       }
7337       else if (nbUniqueNodes == 5 && nbRepl == 3 ) {
7338         //////////////////// HEXAHEDRON ---> pyramid
7339         for ( int iFace = 0; iFace < 6; iFace++ ) {
7340           const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7341           if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7342               curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7343               curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7344             // one face turns into a point ...
7345             int iOppFace = hexa.GetOppFaceIndex( iFace );
7346             ind = hexa.GetFaceNodesIndices( iOppFace );
7347             uniqueNodes.clear();
7348             for ( iCur = 0; iCur < 4; iCur++ ) {
7349               if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7350                 break;
7351               else
7352                 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7353             }
7354             if ( uniqueNodes.size() == 4 ) {
7355               // ... and the opposite one is a quadrangle
7356               // set a top node
7357               const int* indTop = hexa.GetFaceNodesIndices( iFace );
7358               uniqueNodes.push_back( curNodes[indTop[ 0 ]]);
7359               toRemove = false;
7360             }
7361             break;
7362           }
7363         }
7364       }
7365
7366       if ( toRemove && nbUniqueNodes > 4 ) {
7367         ////////////////// HEXAHEDRON ---> polyhedron
7368         hexa.SetExternalNormal();
7369         vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7370         vector<int>                  & quantities = newElemDefs[0].myPolyhedQuantities;
7371         poly_nodes.reserve( 6 * 4 ); poly_nodes.clear();
7372         quantities.reserve( 6 );     quantities.clear();
7373         for ( int iFace = 0; iFace < 6; iFace++ )
7374         {
7375           const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7376           if ( curNodes[ind[0]] == curNodes[ind[2]] ||
7377                curNodes[ind[1]] == curNodes[ind[3]] )
7378           {
7379             quantities.clear();
7380             break; // opposite nodes stick
7381           }
7382           nodeSet.clear();
7383           for ( iCur = 0; iCur < 4; iCur++ )
7384           {
7385             if ( nodeSet.insert( curNodes[ind[ iCur ]] ).second )
7386               poly_nodes.push_back( curNodes[ind[ iCur ]]);
7387           }
7388           if ( nodeSet.size() < 3 )
7389             poly_nodes.resize( poly_nodes.size() - nodeSet.size() );
7390           else
7391             quantities.push_back( nodeSet.size() );
7392         }
7393         if ( quantities.size() >= 4 )
7394         {
7395           nbResElems = 1;
7396           nbUniqueNodes = poly_nodes.size();
7397           newElemDefs[0].SetPoly(true);
7398         }
7399       }
7400       break;
7401     } // case HEXAHEDRON
7402
7403     default:
7404       toRemove = true;
7405
7406     } // switch ( entity )
7407
7408     if ( toRemove && nbResElems == 0 && avoidMakingHoles )
7409     {
7410       // erase from nodeNodeMap nodes whose merge spoils elem
7411       vector< const SMDS_MeshNode* > noMergeNodes;
7412       SMESH_MeshAlgos::DeMerge( elem, curNodes, noMergeNodes );
7413       for ( size_t i = 0; i < noMergeNodes.size(); ++i )
7414         nodeNodeMap.erase( noMergeNodes[i] );
7415     }
7416     
7417   } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7418
7419   uniqueNodes.resize( nbUniqueNodes );
7420
7421   if ( !toRemove && nbResElems == 0 )
7422     nbResElems = 1;
7423
7424   newElemDefs.resize( nbResElems );
7425
7426   return !toRemove;
7427 }
7428
7429
7430 // ========================================================
7431 // class   : ComparableElement
7432 // purpose : allow comparing elements basing on their nodes
7433 // ========================================================
7434
7435 class ComparableElement : public boost::container::flat_set< int >
7436 {
7437   typedef boost::container::flat_set< int >  int_set;
7438
7439   const SMDS_MeshElement* myElem;
7440   int                     mySumID;
7441   mutable int             myGroupID;
7442
7443 public:
7444
7445   ComparableElement( const SMDS_MeshElement* theElem ):
7446     myElem ( theElem ), mySumID( 0 ), myGroupID( -1 )
7447   {
7448     this->reserve( theElem->NbNodes() );
7449     for ( SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator(); nodeIt->more(); )
7450     {
7451       int id = nodeIt->next()->GetID();
7452       mySumID += id;
7453       this->insert( id );
7454     }
7455   }
7456
7457   const SMDS_MeshElement* GetElem() const { return myElem; }
7458
7459   int& GroupID() const { return myGroupID; }
7460   //int& GroupID() const { return const_cast< int& >( myGroupID ); }
7461
7462   ComparableElement( const ComparableElement& theSource ) // move copy
7463   {
7464     ComparableElement& src = const_cast< ComparableElement& >( theSource );
7465     (int_set&) (*this ) = boost::move( src );
7466     myElem    = src.myElem;
7467     mySumID   = src.mySumID;
7468     myGroupID = src.myGroupID;
7469   }
7470
7471   static int HashCode(const ComparableElement& se, int limit )
7472   {
7473     return ::HashCode( se.mySumID, limit );
7474   }
7475   static Standard_Boolean IsEqual(const ComparableElement& se1, const ComparableElement& se2 )
7476   {
7477     return ( se1 == se2 );
7478   }
7479
7480 };
7481
7482 //=======================================================================
7483 //function : FindEqualElements
7484 //purpose  : Return list of group of elements built on the same nodes.
7485 //           Search among theElements or in the whole mesh if theElements is empty
7486 //=======================================================================
7487
7488 void SMESH_MeshEditor::FindEqualElements( TIDSortedElemSet &        theElements,
7489                                           TListOfListOfElementsID & theGroupsOfElementsID )
7490 {
7491   ClearLastCreated();
7492
7493   SMDS_ElemIteratorPtr elemIt;
7494   if ( theElements.empty() ) elemIt = GetMeshDS()->elementsIterator();
7495   else                       elemIt = SMESHUtils::elemSetIterator( theElements );
7496
7497   typedef NCollection_Map< ComparableElement, ComparableElement > TMapOfElements;
7498   typedef std::list<int>                                          TGroupOfElems;
7499   TMapOfElements               mapOfElements;
7500   std::vector< TGroupOfElems > arrayOfGroups;
7501   TGroupOfElems                groupOfElems;
7502
7503   while ( elemIt->more() )
7504   {
7505     const SMDS_MeshElement* curElem = elemIt->next();
7506     if ( curElem->IsNull() )
7507       continue;
7508     ComparableElement      compElem = curElem;
7509     // check uniqueness
7510     const ComparableElement& elemInSet = mapOfElements.Added( compElem );
7511     if ( elemInSet.GetElem() != curElem ) // coincident elem
7512     {
7513       int& iG = elemInSet.GroupID();
7514       if ( iG < 0 )
7515       {
7516         iG = arrayOfGroups.size();
7517         arrayOfGroups.push_back( groupOfElems );
7518         arrayOfGroups[ iG ].push_back( elemInSet.GetElem()->GetID() );
7519       }
7520       arrayOfGroups[ iG ].push_back( curElem->GetID() );
7521     }
7522   }
7523
7524   groupOfElems.clear();
7525   std::vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
7526   for ( ; groupIt != arrayOfGroups.end(); ++groupIt )
7527   {
7528     if ( groupIt->size() > 1 ) {
7529       //groupOfElems.sort(); -- theElements are sorted already
7530       theGroupsOfElementsID.emplace_back( *groupIt );
7531     }
7532   }
7533 }
7534
7535 //=======================================================================
7536 //function : MergeElements
7537 //purpose  : In each given group, substitute all elements by the first one.
7538 //=======================================================================
7539
7540 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
7541 {
7542   ClearLastCreated();
7543
7544   typedef list<int> TListOfIDs;
7545   TListOfIDs rmElemIds; // IDs of elems to remove
7546
7547   SMESHDS_Mesh* aMesh = GetMeshDS();
7548
7549   TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
7550   while ( groupsIt != theGroupsOfElementsID.end() ) {
7551     TListOfIDs& aGroupOfElemID = *groupsIt;
7552     aGroupOfElemID.sort();
7553     int elemIDToKeep = aGroupOfElemID.front();
7554     const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
7555     aGroupOfElemID.pop_front();
7556     TListOfIDs::iterator idIt = aGroupOfElemID.begin();
7557     while ( idIt != aGroupOfElemID.end() ) {
7558       int elemIDToRemove = *idIt;
7559       const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
7560       // add the kept element in groups of removed one (PAL15188)
7561       AddToSameGroups( elemToKeep, elemToRemove, aMesh );
7562       rmElemIds.push_back( elemIDToRemove );
7563       ++idIt;
7564     }
7565     ++groupsIt;
7566   }
7567
7568   Remove( rmElemIds, false );
7569 }
7570
7571 //=======================================================================
7572 //function : MergeEqualElements
7573 //purpose  : Remove all but one of elements built on the same nodes.
7574 //=======================================================================
7575
7576 void SMESH_MeshEditor::MergeEqualElements()
7577 {
7578   TIDSortedElemSet aMeshElements; /* empty input ==
7579                                      to merge equal elements in the whole mesh */
7580   TListOfListOfElementsID aGroupsOfElementsID;
7581   FindEqualElements( aMeshElements, aGroupsOfElementsID );
7582   MergeElements( aGroupsOfElementsID );
7583 }
7584
7585 //=======================================================================
7586 //function : findAdjacentFace
7587 //purpose  :
7588 //=======================================================================
7589
7590 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
7591                                                 const SMDS_MeshNode* n2,
7592                                                 const SMDS_MeshElement* elem)
7593 {
7594   TIDSortedElemSet elemSet, avoidSet;
7595   if ( elem )
7596     avoidSet.insert ( elem );
7597   return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
7598 }
7599
7600 //=======================================================================
7601 //function : findSegment
7602 //purpose  : Return a mesh segment by two nodes one of which can be medium
7603 //=======================================================================
7604
7605 static const SMDS_MeshElement* findSegment(const SMDS_MeshNode* n1,
7606                                            const SMDS_MeshNode* n2)
7607 {
7608   SMDS_ElemIteratorPtr it = n1->GetInverseElementIterator( SMDSAbs_Edge );
7609   while ( it->more() )
7610   {
7611     const SMDS_MeshElement* seg = it->next();
7612     if ( seg->GetNodeIndex( n2 ) >= 0 )
7613       return seg;
7614   }
7615   return 0;
7616 }
7617
7618 //=======================================================================
7619 //function : FindFreeBorder
7620 //purpose  :
7621 //=======================================================================
7622
7623 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
7624
7625 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode*             theFirstNode,
7626                                        const SMDS_MeshNode*             theSecondNode,
7627                                        const SMDS_MeshNode*             theLastNode,
7628                                        list< const SMDS_MeshNode* > &   theNodes,
7629                                        list< const SMDS_MeshElement* >& theFaces)
7630 {
7631   if ( !theFirstNode || !theSecondNode )
7632     return false;
7633   // find border face between theFirstNode and theSecondNode
7634   const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
7635   if ( !curElem )
7636     return false;
7637
7638   theFaces.push_back( curElem );
7639   theNodes.push_back( theFirstNode );
7640   theNodes.push_back( theSecondNode );
7641
7642   const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
7643   //TIDSortedElemSet foundElems;
7644   bool needTheLast = ( theLastNode != 0 );
7645
7646   vector<const SMDS_MeshNode*> nodes;
7647   
7648   while ( nStart != theLastNode ) {
7649     if ( nStart == theFirstNode )
7650       return !needTheLast;
7651
7652     // find all free border faces sharing nStart
7653
7654     list< const SMDS_MeshElement* > curElemList;
7655     list< const SMDS_MeshNode* >    nStartList;
7656     SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
7657     while ( invElemIt->more() ) {
7658       const SMDS_MeshElement* e = invElemIt->next();
7659       //if ( e == curElem || foundElems.insert( e ).second ) // e can encounter twice in border
7660       {
7661         // get nodes
7662         nodes.assign( SMDS_MeshElement::iterator( e->interlacedNodesIterator() ),
7663                       SMDS_MeshElement::iterator() );
7664         nodes.push_back( nodes[ 0 ]);
7665
7666         // check 2 links
7667         int iNode = 0, nbNodes = nodes.size() - 1;
7668         for ( iNode = 0; iNode < nbNodes; iNode++ )
7669           if ((( nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
7670                ( nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
7671               ( ControlFreeBorder( &nodes[ iNode ], e->GetID() )))
7672           {
7673             nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart )]);
7674             curElemList.push_back( e );
7675           }
7676       }
7677     }
7678     // analyse the found
7679
7680     int nbNewBorders = curElemList.size();
7681     if ( nbNewBorders == 0 ) {
7682       // no free border furthermore
7683       return !needTheLast;
7684     }
7685     else if ( nbNewBorders == 1 ) {
7686       // one more element found
7687       nIgnore = nStart;
7688       nStart = nStartList.front();
7689       curElem = curElemList.front();
7690       theFaces.push_back( curElem );
7691       theNodes.push_back( nStart );
7692     }
7693     else {
7694       // several continuations found
7695       list< const SMDS_MeshElement* >::iterator curElemIt;
7696       list< const SMDS_MeshNode* >::iterator nStartIt;
7697       // check if one of them reached the last node
7698       if ( needTheLast ) {
7699         for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7700              curElemIt!= curElemList.end();
7701              curElemIt++, nStartIt++ )
7702           if ( *nStartIt == theLastNode ) {
7703             theFaces.push_back( *curElemIt );
7704             theNodes.push_back( *nStartIt );
7705             return true;
7706           }
7707       }
7708       // find the best free border by the continuations
7709       list<const SMDS_MeshNode*>    contNodes[ 2 ], *cNL;
7710       list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
7711       for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7712            curElemIt!= curElemList.end();
7713            curElemIt++, nStartIt++ )
7714       {
7715         cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
7716         cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
7717         // find one more free border
7718         if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
7719           cNL->clear();
7720           cFL->clear();
7721         }
7722         else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
7723           // choice: clear a worse one
7724           int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
7725           int   iWorse = ( needTheLast ? 1 - iLongest : iLongest );
7726           contNodes[ iWorse ].clear();
7727           contFaces[ iWorse ].clear();
7728         }
7729       }
7730       if ( contNodes[0].empty() && contNodes[1].empty() )
7731         return false;
7732
7733       // push_back the best free border
7734       cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
7735       cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
7736       //theNodes.pop_back(); // remove nIgnore
7737       theNodes.pop_back(); // remove nStart
7738       //theFaces.pop_back(); // remove curElem
7739       theNodes.splice( theNodes.end(), *cNL );
7740       theFaces.splice( theFaces.end(), *cFL );
7741       return true;
7742
7743     } // several continuations found
7744   } // while ( nStart != theLastNode )
7745
7746   return true;
7747 }
7748
7749 //=======================================================================
7750 //function : CheckFreeBorderNodes
7751 //purpose  : Return true if the tree nodes are on a free border
7752 //=======================================================================
7753
7754 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
7755                                             const SMDS_MeshNode* theNode2,
7756                                             const SMDS_MeshNode* theNode3)
7757 {
7758   list< const SMDS_MeshNode* > nodes;
7759   list< const SMDS_MeshElement* > faces;
7760   return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
7761 }
7762
7763 //=======================================================================
7764 //function : SewFreeBorder
7765 //purpose  :
7766 //warning  : for border-to-side sewing theSideSecondNode is considered as
7767 //           the last side node and theSideThirdNode is not used
7768 //=======================================================================
7769
7770 SMESH_MeshEditor::Sew_Error
7771 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
7772                                  const SMDS_MeshNode* theBordSecondNode,
7773                                  const SMDS_MeshNode* theBordLastNode,
7774                                  const SMDS_MeshNode* theSideFirstNode,
7775                                  const SMDS_MeshNode* theSideSecondNode,
7776                                  const SMDS_MeshNode* theSideThirdNode,
7777                                  const bool           theSideIsFreeBorder,
7778                                  const bool           toCreatePolygons,
7779                                  const bool           toCreatePolyedrs)
7780 {
7781   ClearLastCreated();
7782
7783   Sew_Error aResult = SEW_OK;
7784
7785   // ====================================
7786   //    find side nodes and elements
7787   // ====================================
7788
7789   list< const SMDS_MeshNode* >    nSide[ 2 ];
7790   list< const SMDS_MeshElement* > eSide[ 2 ];
7791   list< const SMDS_MeshNode* >::iterator    nIt[ 2 ];
7792   list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
7793
7794   // Free border 1
7795   // --------------
7796   if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
7797                       nSide[0], eSide[0])) {
7798     MESSAGE(" Free Border 1 not found " );
7799     aResult = SEW_BORDER1_NOT_FOUND;
7800   }
7801   if (theSideIsFreeBorder) {
7802     // Free border 2
7803     // --------------
7804     if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
7805                         nSide[1], eSide[1])) {
7806       MESSAGE(" Free Border 2 not found " );
7807       aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
7808     }
7809   }
7810   if ( aResult != SEW_OK )
7811     return aResult;
7812
7813   if (!theSideIsFreeBorder) {
7814     // Side 2
7815     // --------------
7816
7817     // -------------------------------------------------------------------------
7818     // Algo:
7819     // 1. If nodes to merge are not coincident, move nodes of the free border
7820     //    from the coord sys defined by the direction from the first to last
7821     //    nodes of the border to the correspondent sys of the side 2
7822     // 2. On the side 2, find the links most co-directed with the correspondent
7823     //    links of the free border
7824     // -------------------------------------------------------------------------
7825
7826     // 1. Since sewing may break if there are volumes to split on the side 2,
7827     //    we won't move nodes but just compute new coordinates for them
7828     typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
7829     TNodeXYZMap nBordXYZ;
7830     list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
7831     list< const SMDS_MeshNode* >::iterator nBordIt;
7832
7833     gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
7834     gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
7835     gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
7836     gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
7837     double tol2 = 1.e-8;
7838     gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
7839     if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
7840       // Need node movement.
7841
7842       // find X and Z axes to create trsf
7843       gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
7844       gp_Vec X = Zs ^ Zb;
7845       if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
7846         // Zb || Zs
7847         X = gp_Ax2( gp::Origin(), Zb ).XDirection();
7848
7849       // coord systems
7850       gp_Ax3 toBordAx( Pb1, Zb, X );
7851       gp_Ax3 fromSideAx( Ps1, Zs, X );
7852       gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
7853       // set trsf
7854       gp_Trsf toBordSys, fromSide2Sys;
7855       toBordSys.SetTransformation( toBordAx );
7856       fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
7857       fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
7858
7859       // move
7860       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
7861         const SMDS_MeshNode* n = *nBordIt;
7862         gp_XYZ xyz( n->X(),n->Y(),n->Z() );
7863         toBordSys.Transforms( xyz );
7864         fromSide2Sys.Transforms( xyz );
7865         nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
7866       }
7867     }
7868     else {
7869       // just insert nodes XYZ in the nBordXYZ map
7870       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
7871         const SMDS_MeshNode* n = *nBordIt;
7872         nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
7873       }
7874     }
7875
7876     // 2. On the side 2, find the links most co-directed with the correspondent
7877     //    links of the free border
7878
7879     list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
7880     list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
7881     sideNodes.push_back( theSideFirstNode );
7882
7883     bool hasVolumes = false;
7884     LinkID_Gen aLinkID_Gen( GetMeshDS() );
7885     set<long> foundSideLinkIDs, checkedLinkIDs;
7886     SMDS_VolumeTool volume;
7887     //const SMDS_MeshNode* faceNodes[ 4 ];
7888
7889     const SMDS_MeshNode*    sideNode;
7890     const SMDS_MeshElement* sideElem  = 0;
7891     const SMDS_MeshNode* prevSideNode = theSideFirstNode;
7892     const SMDS_MeshNode* prevBordNode = theBordFirstNode;
7893     nBordIt = bordNodes.begin();
7894     nBordIt++;
7895     // border node position and border link direction to compare with
7896     gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
7897     gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
7898     // choose next side node by link direction or by closeness to
7899     // the current border node:
7900     bool searchByDir = ( *nBordIt != theBordLastNode );
7901     do {
7902       // find the next node on the Side 2
7903       sideNode = 0;
7904       double maxDot = -DBL_MAX, minDist = DBL_MAX;
7905       long linkID;
7906       checkedLinkIDs.clear();
7907       gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
7908
7909       // loop on inverse elements of current node (prevSideNode) on the Side 2
7910       SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
7911       while ( invElemIt->more() )
7912       {
7913         const SMDS_MeshElement* elem = invElemIt->next();
7914         // prepare data for a loop on links coming to prevSideNode, of a face or a volume
7915         int iPrevNode = 0, iNode = 0, nbNodes = elem->NbNodes();
7916         vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
7917         bool isVolume = volume.Set( elem );
7918         const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
7919         if ( isVolume ) // --volume
7920           hasVolumes = true;
7921         else if ( elem->GetType() == SMDSAbs_Face ) { // --face
7922           // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
7923           SMDS_NodeIteratorPtr nIt = elem->interlacedNodesIterator();
7924           while ( nIt->more() ) {
7925             nodes[ iNode ] = cast2Node( nIt->next() );
7926             if ( nodes[ iNode++ ] == prevSideNode )
7927               iPrevNode = iNode - 1;
7928           }
7929           // there are 2 links to check
7930           nbNodes = 2;
7931         }
7932         else // --edge
7933           continue;
7934         // loop on links, to be precise, on the second node of links
7935         for ( iNode = 0; iNode < nbNodes; iNode++ ) {
7936           const SMDS_MeshNode* n = nodes[ iNode ];
7937           if ( isVolume ) {
7938             if ( !volume.IsLinked( n, prevSideNode ))
7939               continue;
7940           }
7941           else {
7942             if ( iNode ) // a node before prevSideNode
7943               n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
7944             else         // a node after prevSideNode
7945               n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
7946           }
7947           // check if this link was already used
7948           long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
7949           bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
7950           if (!isJustChecked &&
7951               foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
7952           {
7953             // test a link geometrically
7954             gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
7955             bool linkIsBetter = false;
7956             double dot = 0.0, dist = 0.0;
7957             if ( searchByDir ) { // choose most co-directed link
7958               dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
7959               linkIsBetter = ( dot > maxDot );
7960             }
7961             else { // choose link with the node closest to bordPos
7962               dist = ( nextXYZ - bordPos ).SquareModulus();
7963               linkIsBetter = ( dist < minDist );
7964             }
7965             if ( linkIsBetter ) {
7966               maxDot = dot;
7967               minDist = dist;
7968               linkID = iLink;
7969               sideNode = n;
7970               sideElem = elem;
7971             }
7972           }
7973         }
7974       } // loop on inverse elements of prevSideNode
7975
7976       if ( !sideNode ) {
7977         MESSAGE(" Can't find path by links of the Side 2 ");
7978         return SEW_BAD_SIDE_NODES;
7979       }
7980       sideNodes.push_back( sideNode );
7981       sideElems.push_back( sideElem );
7982       foundSideLinkIDs.insert ( linkID );
7983       prevSideNode = sideNode;
7984
7985       if ( *nBordIt == theBordLastNode )
7986         searchByDir = false;
7987       else {
7988         // find the next border link to compare with
7989         gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
7990         searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
7991         // move to next border node if sideNode is before forward border node (bordPos)
7992         while ( *nBordIt != theBordLastNode && !searchByDir ) {
7993           prevBordNode = *nBordIt;
7994           nBordIt++;
7995           bordPos = nBordXYZ[ *nBordIt ];
7996           bordDir = bordPos - nBordXYZ[ prevBordNode ];
7997           searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
7998         }
7999       }
8000     }
8001     while ( sideNode != theSideSecondNode );
8002
8003     if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8004       MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8005       return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8006     }
8007   } // end nodes search on the side 2
8008
8009   // ============================
8010   // sew the border to the side 2
8011   // ============================
8012
8013   int nbNodes[]  = { (int)nSide[0].size(), (int)nSide[1].size() };
8014   int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8015
8016   bool toMergeConformal = ( nbNodes[0] == nbNodes[1] );
8017   if ( toMergeConformal && toCreatePolygons )
8018   {
8019     // do not merge quadrangles if polygons are OK (IPAL0052824)
8020     eIt[0] = eSide[0].begin();
8021     eIt[1] = eSide[1].begin();
8022     bool allQuads[2] = { true, true };
8023     for ( int iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8024       for ( ; allQuads[iBord] && eIt[iBord] != eSide[iBord].end(); ++eIt[iBord] )
8025         allQuads[iBord] = ( (*eIt[iBord])->NbCornerNodes() == 4 );
8026     }
8027     toMergeConformal = ( !allQuads[0] && !allQuads[1] );
8028   }
8029
8030   TListOfListOfNodes nodeGroupsToMerge;
8031   if (( toMergeConformal ) ||
8032       ( theSideIsFreeBorder && !theSideThirdNode )) {
8033
8034     // all nodes are to be merged
8035
8036     for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8037          nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8038          nIt[0]++, nIt[1]++ )
8039     {
8040       nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8041       nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8042       nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8043     }
8044   }
8045   else {
8046
8047     // insert new nodes into the border and the side to get equal nb of segments
8048
8049     // get normalized parameters of nodes on the borders
8050     vector< double > param[ 2 ];
8051     param[0].resize( maxNbNodes );
8052     param[1].resize( maxNbNodes );
8053     int iNode, iBord;
8054     for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8055       list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8056       list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8057       const SMDS_MeshNode* nPrev = *nIt;
8058       double bordLength = 0;
8059       for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8060         const SMDS_MeshNode* nCur = *nIt;
8061         gp_XYZ segment (nCur->X() - nPrev->X(),
8062                         nCur->Y() - nPrev->Y(),
8063                         nCur->Z() - nPrev->Z());
8064         double segmentLen = segment.Modulus();
8065         bordLength += segmentLen;
8066         param[ iBord ][ iNode ] = bordLength;
8067         nPrev = nCur;
8068       }
8069       // normalize within [0,1]
8070       for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8071         param[ iBord ][ iNode ] /= bordLength;
8072       }
8073     }
8074
8075     // loop on border segments
8076     const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8077     int i[ 2 ] = { 0, 0 };
8078     nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8079     nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8080
8081     // element can be split while iterating on border if it has two edges in the border
8082     std::map< const SMDS_MeshElement* , const SMDS_MeshElement* > elemReplaceMap;
8083     std::map< const SMDS_MeshElement* , const SMDS_MeshElement* >::iterator elemReplaceMapIt;
8084
8085     TElemOfNodeListMap insertMap;
8086     TElemOfNodeListMap::iterator insertMapIt;
8087     // insertMap is
8088     // key:   elem to insert nodes into
8089     // value: 2 nodes to insert between + nodes to be inserted
8090     do {
8091       bool next[ 2 ] = { false, false };
8092
8093       // find min adjacent segment length after sewing
8094       double nextParam = 10., prevParam = 0;
8095       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8096         if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8097           nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8098         if ( i[ iBord ] > 0 )
8099           prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8100       }
8101       double  minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8102       double  maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8103       double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8104
8105       // choose to insert or to merge nodes
8106       double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8107       if ( Abs( du ) <= minSegLen * 0.2 ) {
8108         // merge
8109         // ------
8110         nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8111         const SMDS_MeshNode* n0 = *nIt[0];
8112         const SMDS_MeshNode* n1 = *nIt[1];
8113         nodeGroupsToMerge.back().push_back( n1 );
8114         nodeGroupsToMerge.back().push_back( n0 );
8115         // position of node of the border changes due to merge
8116         param[ 0 ][ i[0] ] += du;
8117         // move n1 for the sake of elem shape evaluation during insertion.
8118         // n1 will be removed by MergeNodes() anyway
8119         const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8120         next[0] = next[1] = true;
8121       }
8122       else {
8123         // insert
8124         // ------
8125         int intoBord = ( du < 0 ) ? 0 : 1;
8126         const SMDS_MeshElement* elem = *eIt [ intoBord ];
8127         const SMDS_MeshNode*    n1   = nPrev[ intoBord ];
8128         const SMDS_MeshNode*    n2   = *nIt [ intoBord ];
8129         const SMDS_MeshNode*    nIns = *nIt [ 1 - intoBord ];
8130         if ( intoBord == 1 ) {
8131           // move node of the border to be on a link of elem of the side
8132           SMESH_NodeXYZ p1( n1 ), p2( n2 );
8133           double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8134           gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8135           GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8136         }
8137         elemReplaceMapIt = elemReplaceMap.find( elem );
8138         if ( elemReplaceMapIt != elemReplaceMap.end() )
8139           elem = elemReplaceMapIt->second;
8140
8141         insertMapIt = insertMap.find( elem );
8142         bool  notFound = ( insertMapIt == insertMap.end() );
8143         bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8144         if ( otherLink ) {
8145           // insert into another link of the same element:
8146           // 1. perform insertion into the other link of the elem
8147           list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8148           const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8149           const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8150           InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8151           // 2. perform insertion into the link of adjacent faces
8152           while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem )) {
8153             InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8154           }
8155           while ( const SMDS_MeshElement* seg = findSegment( n12, n22 )) {
8156             InsertNodesIntoLink( seg, n12, n22, nodeList );
8157           }
8158           if (toCreatePolyedrs) {
8159             // perform insertion into the links of adjacent volumes
8160             UpdateVolumes(n12, n22, nodeList);
8161           }
8162           // 3. find an element appeared on n1 and n2 after the insertion
8163           insertMap.erase( insertMapIt );
8164           const SMDS_MeshElement* elem2 = findAdjacentFace( n1, n2, 0 );
8165           elemReplaceMap.insert( std::make_pair( elem, elem2 ));
8166           elem = elem2;
8167         }
8168         if ( notFound || otherLink ) {
8169           // add element and nodes of the side into the insertMap
8170           insertMapIt = insertMap.insert( make_pair( elem, list<const SMDS_MeshNode*>() )).first;
8171           (*insertMapIt).second.push_back( n1 );
8172           (*insertMapIt).second.push_back( n2 );
8173         }
8174         // add node to be inserted into elem
8175         (*insertMapIt).second.push_back( nIns );
8176         next[ 1 - intoBord ] = true;
8177       }
8178
8179       // go to the next segment
8180       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8181         if ( next[ iBord ] ) {
8182           if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8183             eIt[ iBord ]++;
8184           nPrev[ iBord ] = *nIt[ iBord ];
8185           nIt[ iBord ]++; i[ iBord ]++;
8186         }
8187       }
8188     }
8189     while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8190
8191     // perform insertion of nodes into elements
8192
8193     for (insertMapIt = insertMap.begin();
8194          insertMapIt != insertMap.end();
8195          insertMapIt++ )
8196     {
8197       const SMDS_MeshElement* elem = (*insertMapIt).first;
8198       list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8199       if ( nodeList.size() < 3 ) continue;
8200       const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8201       const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8202
8203       InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8204
8205       while ( const SMDS_MeshElement* seg = findSegment( n1, n2 )) {
8206         InsertNodesIntoLink( seg, n1, n2, nodeList );
8207       }
8208
8209       if ( !theSideIsFreeBorder ) {
8210         // look for and insert nodes into the faces adjacent to elem
8211         while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem )) {
8212           InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8213         }
8214       }
8215       if (toCreatePolyedrs) {
8216         // perform insertion into the links of adjacent volumes
8217         UpdateVolumes(n1, n2, nodeList);
8218       }
8219     }
8220   } // end: insert new nodes
8221
8222   MergeNodes ( nodeGroupsToMerge );
8223
8224
8225   // Remove coincident segments
8226
8227   // get new segments
8228   TIDSortedElemSet segments;
8229   SMESH_SequenceOfElemPtr newFaces;
8230   for ( size_t i = 0; i < myLastCreatedElems.size(); ++i )
8231   {
8232     if ( !myLastCreatedElems[i] ) continue;
8233     if ( myLastCreatedElems[i]->GetType() == SMDSAbs_Edge )
8234       segments.insert( segments.end(), myLastCreatedElems[i] );
8235     else
8236       newFaces.push_back( myLastCreatedElems[i] );
8237   }
8238   // get segments adjacent to merged nodes
8239   TListOfListOfNodes::iterator groupIt = nodeGroupsToMerge.begin();
8240   for ( ; groupIt != nodeGroupsToMerge.end(); groupIt++ )
8241   {
8242     const list<const SMDS_MeshNode*>& nodes = *groupIt;
8243     if ( nodes.front()->IsNull() ) continue;
8244     SMDS_ElemIteratorPtr segIt = nodes.front()->GetInverseElementIterator( SMDSAbs_Edge );
8245     while ( segIt->more() )
8246       segments.insert( segIt->next() );
8247   }
8248
8249   // find coincident
8250   TListOfListOfElementsID equalGroups;
8251   if ( !segments.empty() )
8252     FindEqualElements( segments, equalGroups );
8253   if ( !equalGroups.empty() )
8254   {
8255     // remove from segments those that will be removed
8256     TListOfListOfElementsID::iterator itGroups = equalGroups.begin();
8257     for ( ; itGroups != equalGroups.end(); ++itGroups )
8258     {
8259       list< int >& group = *itGroups;
8260       list< int >::iterator id = group.begin();
8261       for ( ++id; id != group.end(); ++id )
8262         if ( const SMDS_MeshElement* seg = GetMeshDS()->FindElement( *id ))
8263           segments.erase( seg );
8264     }
8265     // remove equal segments
8266     MergeElements( equalGroups );
8267
8268     // restore myLastCreatedElems
8269     myLastCreatedElems = newFaces;
8270     TIDSortedElemSet::iterator seg = segments.begin();
8271     for ( ; seg != segments.end(); ++seg )
8272       myLastCreatedElems.push_back( *seg );
8273   }
8274
8275   return aResult;
8276 }
8277
8278 //=======================================================================
8279 //function : InsertNodesIntoLink
8280 //purpose  : insert theNodesToInsert into theElement between theBetweenNode1
8281 //           and theBetweenNode2 and split theElement
8282 //=======================================================================
8283
8284 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement*     theElement,
8285                                            const SMDS_MeshNode*        theBetweenNode1,
8286                                            const SMDS_MeshNode*        theBetweenNode2,
8287                                            list<const SMDS_MeshNode*>& theNodesToInsert,
8288                                            const bool                  toCreatePoly)
8289 {
8290   if ( !theElement ) return;
8291
8292   SMESHDS_Mesh *aMesh = GetMeshDS();
8293   vector<const SMDS_MeshElement*> newElems;
8294
8295   if ( theElement->GetType() == SMDSAbs_Edge )
8296   {
8297     theNodesToInsert.push_front( theBetweenNode1 );
8298     theNodesToInsert.push_back ( theBetweenNode2 );
8299     list<const SMDS_MeshNode*>::iterator n = theNodesToInsert.begin();
8300     const SMDS_MeshNode* n1 = *n;
8301     for ( ++n; n != theNodesToInsert.end(); ++n )
8302     {
8303       const SMDS_MeshNode* n2 = *n;
8304       if ( const SMDS_MeshElement* seg = aMesh->FindEdge( n1, n2 ))
8305         AddToSameGroups( seg, theElement, aMesh );
8306       else
8307         newElems.push_back( aMesh->AddEdge ( n1, n2 ));
8308       n1 = n2;
8309     }
8310     theNodesToInsert.pop_front();
8311     theNodesToInsert.pop_back();
8312
8313     if ( theElement->IsQuadratic() ) // add a not split part
8314     {
8315       vector<const SMDS_MeshNode*> nodes( theElement->begin_nodes(),
8316                                           theElement->end_nodes() );
8317       int iOther = 0, nbN = nodes.size();
8318       for ( ; iOther < nbN; ++iOther )
8319         if ( nodes[iOther] != theBetweenNode1 &&
8320              nodes[iOther] != theBetweenNode2 )
8321           break;
8322       if      ( iOther == 0 )
8323       {
8324         if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[0], nodes[1] ))
8325           AddToSameGroups( seg, theElement, aMesh );
8326         else
8327           newElems.push_back( aMesh->AddEdge ( nodes[0], nodes[1] ));
8328       }
8329       else if ( iOther == 2 )
8330       {
8331         if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[1], nodes[2] ))
8332           AddToSameGroups( seg, theElement, aMesh );
8333         else
8334           newElems.push_back( aMesh->AddEdge ( nodes[1], nodes[2] ));
8335       }
8336     }
8337     // treat new elements
8338     for ( size_t i = 0; i < newElems.size(); ++i )
8339       if ( newElems[i] )
8340       {
8341         aMesh->SetMeshElementOnShape( newElems[i], theElement->getshapeId() );
8342         myLastCreatedElems.push_back( newElems[i] );
8343       }
8344     ReplaceElemInGroups( theElement, newElems, aMesh );
8345     aMesh->RemoveElement( theElement );
8346     return;
8347
8348   } // if ( theElement->GetType() == SMDSAbs_Edge )
8349
8350   const SMDS_MeshElement* theFace = theElement;
8351   if ( theFace->GetType() != SMDSAbs_Face ) return;
8352
8353   // find indices of 2 link nodes and of the rest nodes
8354   int iNode = 0, il1, il2, i3, i4;
8355   il1 = il2 = i3 = i4 = -1;
8356   vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8357
8358   SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8359   while ( nodeIt->more() ) {
8360     const SMDS_MeshNode* n = nodeIt->next();
8361     if ( n == theBetweenNode1 )
8362       il1 = iNode;
8363     else if ( n == theBetweenNode2 )
8364       il2 = iNode;
8365     else if ( i3 < 0 )
8366       i3 = iNode;
8367     else
8368       i4 = iNode;
8369     nodes[ iNode++ ] = n;
8370   }
8371   if ( il1 < 0 || il2 < 0 || i3 < 0 )
8372     return ;
8373
8374   // arrange link nodes to go one after another regarding the face orientation
8375   bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8376   list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8377   if ( reverse ) {
8378     iNode = il1;
8379     il1 = il2;
8380     il2 = iNode;
8381     aNodesToInsert.reverse();
8382   }
8383   // check that not link nodes of a quadrangles are in good order
8384   int nbFaceNodes = theFace->NbNodes();
8385   if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8386     iNode = i3;
8387     i3 = i4;
8388     i4 = iNode;
8389   }
8390
8391   if (toCreatePoly || theFace->IsPoly()) {
8392
8393     iNode = 0;
8394     vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8395
8396     // add nodes of face up to first node of link
8397     bool isFLN = false;
8398     SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8399     while ( nodeIt->more() && !isFLN ) {
8400       const SMDS_MeshNode* n = nodeIt->next();
8401       poly_nodes[iNode++] = n;
8402       isFLN = ( n == nodes[il1] );
8403     }
8404     // add nodes to insert
8405     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8406     for (; nIt != aNodesToInsert.end(); nIt++) {
8407       poly_nodes[iNode++] = *nIt;
8408     }
8409     // add nodes of face starting from last node of link
8410     while ( nodeIt->more() ) {
8411       const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8412       poly_nodes[iNode++] = n;
8413     }
8414
8415     // make a new face
8416     newElems.push_back( aMesh->AddPolygonalFace( poly_nodes ));
8417   }
8418
8419   else if ( !theFace->IsQuadratic() )
8420   {
8421     // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8422     int nbLinkNodes = 2 + aNodesToInsert.size();
8423     //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8424     vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8425     linkNodes[ 0 ] = nodes[ il1 ];
8426     linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8427     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8428     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8429       linkNodes[ iNode++ ] = *nIt;
8430     }
8431     // decide how to split a quadrangle: compare possible variants
8432     // and choose which of splits to be a quadrangle
8433     int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad = 0;
8434     if ( nbFaceNodes == 3 ) {
8435       iBestQuad = nbSplits;
8436       i4 = i3;
8437     }
8438     else if ( nbFaceNodes == 4 ) {
8439       SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8440       double aBestRate = DBL_MAX;
8441       for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8442         i1 = 0; i2 = 1;
8443         double aBadRate = 0;
8444         // evaluate elements quality
8445         for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8446           if ( iSplit == iQuad ) {
8447             SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8448                                    linkNodes[ i2++ ],
8449                                    nodes[ i3 ],
8450                                    nodes[ i4 ]);
8451             aBadRate += getBadRate( &quad, aCrit );
8452           }
8453           else {
8454             SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8455                                    linkNodes[ i2++ ],
8456                                    nodes[ iSplit < iQuad ? i4 : i3 ]);
8457             aBadRate += getBadRate( &tria, aCrit );
8458           }
8459         }
8460         // choice
8461         if ( aBadRate < aBestRate ) {
8462           iBestQuad = iQuad;
8463           aBestRate = aBadRate;
8464         }
8465       }
8466     }
8467
8468     // create new elements
8469     i1 = 0; i2 = 1;
8470     for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ )
8471     {
8472       if ( iSplit == iBestQuad )
8473         newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8474                                             linkNodes[ i2++ ],
8475                                             nodes[ i3 ],
8476                                             nodes[ i4 ]));
8477       else
8478         newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8479                                             linkNodes[ i2++ ],
8480                                             nodes[ iSplit < iBestQuad ? i4 : i3 ]));
8481     }
8482
8483     const SMDS_MeshNode* newNodes[ 4 ];
8484     newNodes[ 0 ] = linkNodes[ i1 ];
8485     newNodes[ 1 ] = linkNodes[ i2 ];
8486     newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
8487     newNodes[ 3 ] = nodes[ i4 ];
8488     if (iSplit == iBestQuad)
8489       newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] ));
8490     else
8491       newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] ));
8492
8493   } // end if(!theFace->IsQuadratic())
8494
8495   else { // theFace is quadratic
8496     // we have to split theFace on simple triangles and one simple quadrangle
8497     int tmp = il1/2;
8498     int nbshift = tmp*2;
8499     // shift nodes in nodes[] by nbshift
8500     int i,j;
8501     for(i=0; i<nbshift; i++) {
8502       const SMDS_MeshNode* n = nodes[0];
8503       for(j=0; j<nbFaceNodes-1; j++) {
8504         nodes[j] = nodes[j+1];
8505       }
8506       nodes[nbFaceNodes-1] = n;
8507     }
8508     il1 = il1 - nbshift;
8509     // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
8510     //   n0      n1     n2    n0      n1     n2
8511     //     +-----+-----+        +-----+-----+
8512     //      \         /         |           |
8513     //       \       /          |           |
8514     //      n5+     +n3       n7+           +n3
8515     //         \   /            |           |
8516     //          \ /             |           |
8517     //           +              +-----+-----+
8518     //           n4           n6      n5     n4
8519
8520     // create new elements
8521     int n1,n2,n3;
8522     if ( nbFaceNodes == 6 ) { // quadratic triangle
8523       newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8524       if ( theFace->IsMediumNode(nodes[il1]) ) {
8525         // create quadrangle
8526         newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[5] ));
8527         n1 = 1;
8528         n2 = 2;
8529         n3 = 3;
8530       }
8531       else {
8532         // create quadrangle
8533         newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[5] ));
8534         n1 = 0;
8535         n2 = 1;
8536         n3 = 5;
8537       }
8538     }
8539     else { // nbFaceNodes==8 - quadratic quadrangle
8540       newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8541       newElems.push_back( aMesh->AddFace( nodes[5], nodes[6], nodes[7] ));
8542       newElems.push_back( aMesh->AddFace( nodes[5], nodes[7], nodes[3] ));
8543       if ( theFace->IsMediumNode( nodes[ il1 ])) {
8544         // create quadrangle
8545         newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[7] ));
8546         n1 = 1;
8547         n2 = 2;
8548         n3 = 3;
8549       }
8550       else {
8551         // create quadrangle
8552         newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[7] ));
8553         n1 = 0;
8554         n2 = 1;
8555         n3 = 7;
8556       }
8557     }
8558     // create needed triangles using n1,n2,n3 and inserted nodes
8559     int nbn = 2 + aNodesToInsert.size();
8560     vector<const SMDS_MeshNode*> aNodes(nbn);
8561     aNodes[0    ] = nodes[n1];
8562     aNodes[nbn-1] = nodes[n2];
8563     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8564     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8565       aNodes[iNode++] = *nIt;
8566     }
8567     for ( i = 1; i < nbn; i++ )
8568       newElems.push_back( aMesh->AddFace( aNodes[i-1], aNodes[i], nodes[n3] ));
8569   }
8570
8571   // remove the old face
8572   for ( size_t i = 0; i < newElems.size(); ++i )
8573     if ( newElems[i] )
8574     {
8575       aMesh->SetMeshElementOnShape( newElems[i], theFace->getshapeId() );
8576       myLastCreatedElems.push_back( newElems[i] );
8577     }
8578   ReplaceElemInGroups( theFace, newElems, aMesh );
8579   aMesh->RemoveElement(theFace);
8580
8581 } // InsertNodesIntoLink()
8582
8583 //=======================================================================
8584 //function : UpdateVolumes
8585 //purpose  :
8586 //=======================================================================
8587
8588 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode*        theBetweenNode1,
8589                                       const SMDS_MeshNode*        theBetweenNode2,
8590                                       list<const SMDS_MeshNode*>& theNodesToInsert)
8591 {
8592   ClearLastCreated();
8593
8594   SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
8595   while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
8596     const SMDS_MeshElement* elem = invElemIt->next();
8597
8598     // check, if current volume has link theBetweenNode1 - theBetweenNode2
8599     SMDS_VolumeTool aVolume (elem);
8600     if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
8601       continue;
8602
8603     // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
8604     int iface, nbFaces = aVolume.NbFaces();
8605     vector<const SMDS_MeshNode *> poly_nodes;
8606     vector<int> quantities (nbFaces);
8607
8608     for (iface = 0; iface < nbFaces; iface++) {
8609       int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
8610       // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
8611       const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
8612
8613       for (int inode = 0; inode < nbFaceNodes; inode++) {
8614         poly_nodes.push_back(faceNodes[inode]);
8615
8616         if (nbInserted == 0) {
8617           if (faceNodes[inode] == theBetweenNode1) {
8618             if (faceNodes[inode + 1] == theBetweenNode2) {
8619               nbInserted = theNodesToInsert.size();
8620
8621               // add nodes to insert
8622               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
8623               for (; nIt != theNodesToInsert.end(); nIt++) {
8624                 poly_nodes.push_back(*nIt);
8625               }
8626             }
8627           }
8628           else if (faceNodes[inode] == theBetweenNode2) {
8629             if (faceNodes[inode + 1] == theBetweenNode1) {
8630               nbInserted = theNodesToInsert.size();
8631
8632               // add nodes to insert in reversed order
8633               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
8634               nIt--;
8635               for (; nIt != theNodesToInsert.begin(); nIt--) {
8636                 poly_nodes.push_back(*nIt);
8637               }
8638               poly_nodes.push_back(*nIt);
8639             }
8640           }
8641           else {
8642           }
8643         }
8644       }
8645       quantities[iface] = nbFaceNodes + nbInserted;
8646     }
8647
8648     // Replace the volume
8649     SMESHDS_Mesh *aMesh = GetMeshDS();
8650
8651     if ( SMDS_MeshElement* newElem = aMesh->AddPolyhedralVolume( poly_nodes, quantities ))
8652     {
8653       aMesh->SetMeshElementOnShape( newElem, elem->getshapeId() );
8654       myLastCreatedElems.push_back( newElem );
8655       ReplaceElemInGroups( elem, newElem, aMesh );
8656     }
8657     aMesh->RemoveElement( elem );
8658   }
8659 }
8660
8661 namespace
8662 {
8663   //================================================================================
8664   /*!
8665    * \brief Transform any volume into data of SMDSEntity_Polyhedra
8666    */
8667   //================================================================================
8668
8669   void volumeToPolyhedron( const SMDS_MeshElement*         elem,
8670                            vector<const SMDS_MeshNode *> & nodes,
8671                            vector<int> &                   nbNodeInFaces )
8672   {
8673     nodes.clear();
8674     nbNodeInFaces.clear();
8675     SMDS_VolumeTool vTool ( elem );
8676     for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
8677     {
8678       const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
8679       nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
8680       nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
8681     }
8682   }
8683 }
8684
8685 //=======================================================================
8686 /*!
8687  * \brief Convert elements contained in a sub-mesh to quadratic
8688  * \return int - nb of checked elements
8689  */
8690 //=======================================================================
8691
8692 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh *   theSm,
8693                                              SMESH_MesherHelper& theHelper,
8694                                              const bool          theForce3d)
8695 {
8696   //MESSAGE("convertElemToQuadratic");
8697   int nbElem = 0;
8698   if( !theSm ) return nbElem;
8699
8700   vector<int> nbNodeInFaces;
8701   vector<const SMDS_MeshNode *> nodes;
8702   SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
8703   while(ElemItr->more())
8704   {
8705     nbElem++;
8706     const SMDS_MeshElement* elem = ElemItr->next();
8707     if( !elem ) continue;
8708
8709     // analyse a necessity of conversion
8710     const SMDSAbs_ElementType aType = elem->GetType();
8711     if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
8712       continue;
8713     const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
8714     bool hasCentralNodes = false;
8715     if ( elem->IsQuadratic() )
8716     {
8717       bool alreadyOK;
8718       switch ( aGeomType ) {
8719       case SMDSEntity_Quad_Triangle:
8720       case SMDSEntity_Quad_Quadrangle:
8721       case SMDSEntity_Quad_Hexa:
8722       case SMDSEntity_Quad_Penta:
8723         alreadyOK = !theHelper.GetIsBiQuadratic(); break;
8724
8725       case SMDSEntity_BiQuad_Triangle:
8726       case SMDSEntity_BiQuad_Quadrangle:
8727       case SMDSEntity_TriQuad_Hexa:
8728       case SMDSEntity_BiQuad_Penta:
8729         alreadyOK = theHelper.GetIsBiQuadratic();
8730         hasCentralNodes = true;
8731         break;
8732       default:
8733         alreadyOK = true;
8734       }
8735       // take into account already present medium nodes
8736       switch ( aType ) {
8737       case SMDSAbs_Volume:
8738         theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
8739       case SMDSAbs_Face:
8740         theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
8741       case SMDSAbs_Edge:
8742         theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
8743       default:;
8744       }
8745       if ( alreadyOK )
8746         continue;
8747     }
8748     // get elem data needed to re-create it
8749     //
8750     const int id      = elem->GetID();
8751     const int nbNodes = elem->NbCornerNodes();
8752     nodes.assign(elem->begin_nodes(), elem->end_nodes());
8753     if ( aGeomType == SMDSEntity_Polyhedra )
8754       nbNodeInFaces = static_cast<const SMDS_MeshVolume* >( elem )->GetQuantities();
8755     else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
8756       volumeToPolyhedron( elem, nodes, nbNodeInFaces );
8757
8758     // remove a linear element
8759     GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
8760
8761     // remove central nodes of biquadratic elements (biquad->quad conversion)
8762     if ( hasCentralNodes )
8763       for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
8764         if ( nodes[i]->NbInverseElements() == 0 )
8765           GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
8766
8767     const SMDS_MeshElement* NewElem = 0;
8768
8769     switch( aType )
8770     {
8771     case SMDSAbs_Edge :
8772     {
8773       NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
8774       break;
8775     }
8776     case SMDSAbs_Face :
8777     {
8778       switch(nbNodes)
8779       {
8780       case 3:
8781         NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
8782         break;
8783       case 4:
8784         NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8785         break;
8786       default:
8787         NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
8788       }
8789       break;
8790     }
8791     case SMDSAbs_Volume :
8792     {
8793       switch( aGeomType )
8794       {
8795       case SMDSEntity_Tetra:
8796         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8797         break;
8798       case SMDSEntity_Pyramid:
8799         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
8800         break;
8801       case SMDSEntity_Penta:
8802       case SMDSEntity_Quad_Penta:
8803       case SMDSEntity_BiQuad_Penta:
8804         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
8805         break;
8806       case SMDSEntity_Hexa:
8807       case SMDSEntity_Quad_Hexa:
8808       case SMDSEntity_TriQuad_Hexa:
8809         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
8810                                       nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
8811         break;
8812       case SMDSEntity_Hexagonal_Prism:
8813       default:
8814         NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
8815       }
8816       break;
8817     }
8818     default :
8819       continue;
8820     }
8821     ReplaceElemInGroups( elem, NewElem, GetMeshDS());
8822     if( NewElem && NewElem->getshapeId() < 1 )
8823       theSm->AddElement( NewElem );
8824   }
8825   return nbElem;
8826 }
8827 //=======================================================================
8828 //function : ConvertToQuadratic
8829 //purpose  :
8830 //=======================================================================
8831
8832 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
8833 {
8834   //MESSAGE("ConvertToQuadratic "<< theForce3d << " " << theToBiQuad);
8835   SMESHDS_Mesh* meshDS = GetMeshDS();
8836
8837   SMESH_MesherHelper aHelper(*myMesh);
8838
8839   aHelper.SetIsQuadratic( true );
8840   aHelper.SetIsBiQuadratic( theToBiQuad );
8841   aHelper.SetElementsOnShape(true);
8842   aHelper.ToFixNodeParameters( true );
8843
8844   // convert elements assigned to sub-meshes
8845   int nbCheckedElems = 0;
8846   if ( myMesh->HasShapeToMesh() )
8847   {
8848     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
8849     {
8850       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
8851       while ( smIt->more() ) {
8852         SMESH_subMesh* sm = smIt->next();
8853         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
8854           aHelper.SetSubShape( sm->GetSubShape() );
8855           nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
8856         }
8857       }
8858     }
8859   }
8860
8861   // convert elements NOT assigned to sub-meshes
8862   int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
8863   if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
8864   {
8865     aHelper.SetElementsOnShape(false);
8866     SMESHDS_SubMesh *smDS = 0;
8867
8868     // convert edges
8869     SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
8870     while( aEdgeItr->more() )
8871     {
8872       const SMDS_MeshEdge* edge = aEdgeItr->next();
8873       if ( !edge->IsQuadratic() )
8874       {
8875         int                  id = edge->GetID();
8876         const SMDS_MeshNode* n1 = edge->GetNode(0);
8877         const SMDS_MeshNode* n2 = edge->GetNode(1);
8878
8879         meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
8880
8881         const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
8882         ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
8883       }
8884       else
8885       {
8886         aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
8887       }
8888     }
8889
8890     // convert faces
8891     SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
8892     while( aFaceItr->more() )
8893     {
8894       const SMDS_MeshFace* face = aFaceItr->next();
8895       if ( !face ) continue;
8896       
8897       const SMDSAbs_EntityType type = face->GetEntityType();
8898       bool alreadyOK;
8899       switch( type )
8900       {
8901       case SMDSEntity_Quad_Triangle:
8902       case SMDSEntity_Quad_Quadrangle:
8903         alreadyOK = !theToBiQuad;
8904         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
8905         break;
8906       case SMDSEntity_BiQuad_Triangle:
8907       case SMDSEntity_BiQuad_Quadrangle:
8908         alreadyOK = theToBiQuad;
8909         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
8910         break;
8911       default: alreadyOK = false;
8912       }
8913       if ( alreadyOK )
8914         continue;
8915
8916       const int id = face->GetID();
8917       vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
8918
8919       meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
8920
8921       SMDS_MeshFace * NewFace = 0;
8922       switch( type )
8923       {
8924       case SMDSEntity_Triangle:
8925       case SMDSEntity_Quad_Triangle:
8926       case SMDSEntity_BiQuad_Triangle:
8927         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
8928         if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
8929           GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
8930         break;
8931
8932       case SMDSEntity_Quadrangle:
8933       case SMDSEntity_Quad_Quadrangle:
8934       case SMDSEntity_BiQuad_Quadrangle:
8935         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8936         if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
8937           GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
8938         break;
8939
8940       default:;
8941         NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
8942       }
8943       ReplaceElemInGroups( face, NewFace, GetMeshDS());
8944     }
8945
8946     // convert volumes
8947     vector<int> nbNodeInFaces;
8948     SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
8949     while(aVolumeItr->more())
8950     {
8951       const SMDS_MeshVolume* volume = aVolumeItr->next();
8952       if ( !volume ) continue;
8953
8954       const SMDSAbs_EntityType type = volume->GetEntityType();
8955       if ( volume->IsQuadratic() )
8956       {
8957         bool alreadyOK;
8958         switch ( type )
8959         {
8960         case SMDSEntity_Quad_Hexa:    alreadyOK = !theToBiQuad; break;
8961         case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
8962         case SMDSEntity_Quad_Penta:   alreadyOK = !theToBiQuad; break;
8963         case SMDSEntity_BiQuad_Penta: alreadyOK = theToBiQuad; break;
8964         default:                      alreadyOK = true;
8965         }
8966         if ( alreadyOK )
8967         {
8968           aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
8969           continue;
8970         }
8971       }
8972       const int id = volume->GetID();
8973       vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
8974       if ( type == SMDSEntity_Polyhedra )
8975         nbNodeInFaces = static_cast<const SMDS_MeshVolume* >(volume)->GetQuantities();
8976       else if ( type == SMDSEntity_Hexagonal_Prism )
8977         volumeToPolyhedron( volume, nodes, nbNodeInFaces );
8978
8979       meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
8980
8981       SMDS_MeshVolume * NewVolume = 0;
8982       switch ( type )
8983       {
8984       case SMDSEntity_Tetra:
8985         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
8986         break;
8987       case SMDSEntity_Hexa:
8988       case SMDSEntity_Quad_Hexa:
8989       case SMDSEntity_TriQuad_Hexa:
8990         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
8991                                       nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
8992         for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
8993           if ( nodes[i]->NbInverseElements() == 0 )
8994             GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
8995         break;
8996       case SMDSEntity_Pyramid:
8997         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
8998                                       nodes[3], nodes[4], id, theForce3d);
8999         break;
9000       case SMDSEntity_Penta:
9001       case SMDSEntity_Quad_Penta:
9002       case SMDSEntity_BiQuad_Penta:
9003         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9004                                       nodes[3], nodes[4], nodes[5], id, theForce3d);
9005         for ( size_t i = 15; 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_Hexagonal_Prism:
9010       default:
9011         NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9012       }
9013       ReplaceElemInGroups(volume, NewVolume, meshDS);
9014     }
9015   }
9016
9017   if ( !theForce3d )
9018   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9019     // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9020     // aHelper.FixQuadraticElements(myError);
9021     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9022   }
9023 }
9024
9025 //================================================================================
9026 /*!
9027  * \brief Makes given elements quadratic
9028  *  \param theForce3d - if true, the medium nodes will be placed in the middle of link
9029  *  \param theElements - elements to make quadratic
9030  */
9031 //================================================================================
9032
9033 void SMESH_MeshEditor::ConvertToQuadratic(const bool        theForce3d,
9034                                           TIDSortedElemSet& theElements,
9035                                           const bool        theToBiQuad)
9036 {
9037   if ( theElements.empty() ) return;
9038
9039   // we believe that all theElements are of the same type
9040   const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9041
9042   // get all nodes shared by theElements
9043   TIDSortedNodeSet allNodes;
9044   TIDSortedElemSet::iterator eIt = theElements.begin();
9045   for ( ; eIt != theElements.end(); ++eIt )
9046     allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9047
9048   // complete theElements with elements of lower dim whose all nodes are in allNodes
9049
9050   TIDSortedElemSet quadAdjacentElems    [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9051   TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9052   TIDSortedNodeSet::iterator nIt = allNodes.begin();
9053   for ( ; nIt != allNodes.end(); ++nIt )
9054   {
9055     const SMDS_MeshNode* n = *nIt;
9056     SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9057     while ( invIt->more() )
9058     {
9059       const SMDS_MeshElement*      e = invIt->next();
9060       const SMDSAbs_ElementType type = e->GetType();
9061       if ( e->IsQuadratic() )
9062       {
9063         quadAdjacentElems[ type ].insert( e );
9064
9065         bool alreadyOK;
9066         switch ( e->GetEntityType() ) {
9067         case SMDSEntity_Quad_Triangle:
9068         case SMDSEntity_Quad_Quadrangle:
9069         case SMDSEntity_Quad_Hexa:         alreadyOK = !theToBiQuad; break;
9070         case SMDSEntity_BiQuad_Triangle:
9071         case SMDSEntity_BiQuad_Quadrangle:
9072         case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; break;
9073         default:                           alreadyOK = true;
9074         }
9075         if ( alreadyOK )
9076           continue;
9077       }
9078       if ( type >= elemType )
9079         continue; // same type or more complex linear element
9080
9081       if ( !checkedAdjacentElems[ type ].insert( e ).second )
9082         continue; // e is already checked
9083
9084       // check nodes
9085       bool allIn = true;
9086       SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9087       while ( nodeIt->more() && allIn )
9088         allIn = allNodes.count( nodeIt->next() );
9089       if ( allIn )
9090         theElements.insert(e );
9091     }
9092   }
9093
9094   SMESH_MesherHelper helper(*myMesh);
9095   helper.SetIsQuadratic( true );
9096   helper.SetIsBiQuadratic( theToBiQuad );
9097
9098   // add links of quadratic adjacent elements to the helper
9099
9100   if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9101     for ( eIt  = quadAdjacentElems[SMDSAbs_Edge].begin();
9102           eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9103     {
9104       helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9105     }
9106   if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9107     for ( eIt  = quadAdjacentElems[SMDSAbs_Face].begin();
9108           eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9109     {
9110       helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9111     }
9112   if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9113     for ( eIt  = quadAdjacentElems[SMDSAbs_Volume].begin();
9114           eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9115     {
9116       helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9117     }
9118
9119   // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9120
9121   SMESHDS_Mesh*  meshDS = GetMeshDS();
9122   SMESHDS_SubMesh* smDS = 0;
9123   for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9124   {
9125     const SMDS_MeshElement* elem = *eIt;
9126
9127     bool alreadyOK;
9128     int nbCentralNodes = 0;
9129     switch ( elem->GetEntityType() ) {
9130       // linear convertible
9131     case SMDSEntity_Edge:
9132     case SMDSEntity_Triangle:
9133     case SMDSEntity_Quadrangle:
9134     case SMDSEntity_Tetra:
9135     case SMDSEntity_Pyramid:
9136     case SMDSEntity_Hexa:
9137     case SMDSEntity_Penta:             alreadyOK = false;       nbCentralNodes = 0; break;
9138       // quadratic that can become bi-quadratic
9139     case SMDSEntity_Quad_Triangle:
9140     case SMDSEntity_Quad_Quadrangle:
9141     case SMDSEntity_Quad_Hexa:         alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9142       // bi-quadratic
9143     case SMDSEntity_BiQuad_Triangle:
9144     case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9145     case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9146       // the rest
9147     default:                           alreadyOK = true;
9148     }
9149     if ( alreadyOK ) continue;
9150
9151     const SMDSAbs_ElementType type = elem->GetType();
9152     const int                   id = elem->GetID();
9153     const int              nbNodes = elem->NbCornerNodes();
9154     vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9155
9156     helper.SetSubShape( elem->getshapeId() );
9157
9158     if ( !smDS || !smDS->Contains( elem ))
9159       smDS = meshDS->MeshElements( elem->getshapeId() );
9160     meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9161
9162     SMDS_MeshElement * newElem = 0;
9163     switch( nbNodes )
9164     {
9165     case 4: // cases for most frequently used element types go first (for optimization)
9166       if ( type == SMDSAbs_Volume )
9167         newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9168       else
9169         newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9170       break;
9171     case 8:
9172       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9173                                  nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9174       break;
9175     case 3:
9176       newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], id, theForce3d);
9177       break;
9178     case 2:
9179       newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9180       break;
9181     case 5:
9182       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9183                                  nodes[4], id, theForce3d);
9184       break;
9185     case 6:
9186       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9187                                  nodes[4], nodes[5], id, theForce3d);
9188       break;
9189     default:;
9190     }
9191     ReplaceElemInGroups( elem, newElem, meshDS);
9192     if( newElem && smDS )
9193       smDS->AddElement( newElem );
9194
9195     // remove central nodes
9196     for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9197       if ( nodes[i]->NbInverseElements() == 0 )
9198         meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9199
9200   } // loop on theElements
9201
9202   if ( !theForce3d )
9203   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9204     // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9205     // helper.FixQuadraticElements( myError );
9206     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9207   }
9208 }
9209
9210 //=======================================================================
9211 /*!
9212  * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9213  * \return int - nb of checked elements
9214  */
9215 //=======================================================================
9216
9217 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh *    theSm,
9218                                      SMDS_ElemIteratorPtr theItr,
9219                                      const int            theShapeID)
9220 {
9221   int nbElem = 0;
9222   SMESHDS_Mesh* meshDS = GetMeshDS();
9223   ElemFeatures elemType;
9224   vector<const SMDS_MeshNode *> nodes;
9225
9226   while( theItr->more() )
9227   {
9228     const SMDS_MeshElement* elem = theItr->next();
9229     nbElem++;
9230     if( elem && elem->IsQuadratic())
9231     {
9232       // get elem data
9233       int nbCornerNodes = elem->NbCornerNodes();
9234       nodes.assign( elem->begin_nodes(), elem->end_nodes() );
9235
9236       elemType.Init( elem, /*basicOnly=*/false ).SetID( elem->GetID() ).SetQuad( false );
9237
9238       //remove a quadratic element
9239       if ( !theSm || !theSm->Contains( elem ))
9240         theSm = meshDS->MeshElements( elem->getshapeId() );
9241       meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9242
9243       // remove medium nodes
9244       for ( size_t i = nbCornerNodes; i < nodes.size(); ++i )
9245         if ( nodes[i]->NbInverseElements() == 0 )
9246           meshDS->RemoveFreeNode( nodes[i], theSm );
9247
9248       // add a linear element
9249       nodes.resize( nbCornerNodes );
9250       SMDS_MeshElement * newElem = AddElement( nodes, elemType );
9251       ReplaceElemInGroups(elem, newElem, meshDS);
9252       if( theSm && newElem )
9253         theSm->AddElement( newElem );
9254     }
9255   }
9256   return nbElem;
9257 }
9258
9259 //=======================================================================
9260 //function : ConvertFromQuadratic
9261 //purpose  :
9262 //=======================================================================
9263
9264 bool SMESH_MeshEditor::ConvertFromQuadratic()
9265 {
9266   int nbCheckedElems = 0;
9267   if ( myMesh->HasShapeToMesh() )
9268   {
9269     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9270     {
9271       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9272       while ( smIt->more() ) {
9273         SMESH_subMesh* sm = smIt->next();
9274         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9275           nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9276       }
9277     }
9278   }
9279
9280   int totalNbElems =
9281     GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9282   if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9283   {
9284     SMESHDS_SubMesh *aSM = 0;
9285     removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9286   }
9287
9288   return true;
9289 }
9290
9291 namespace
9292 {
9293   //================================================================================
9294   /*!
9295    * \brief Return true if all medium nodes of the element are in the node set
9296    */
9297   //================================================================================
9298
9299   bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9300   {
9301     for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9302       if ( !nodeSet.count( elem->GetNode(i) ))
9303         return false;
9304     return true;
9305   }
9306 }
9307
9308 //================================================================================
9309 /*!
9310  * \brief Makes given elements linear
9311  */
9312 //================================================================================
9313
9314 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9315 {
9316   if ( theElements.empty() ) return;
9317
9318   // collect IDs of medium nodes of theElements; some of these nodes will be removed
9319   set<int> mediumNodeIDs;
9320   TIDSortedElemSet::iterator eIt = theElements.begin();
9321   for ( ; eIt != theElements.end(); ++eIt )
9322   {
9323     const SMDS_MeshElement* e = *eIt;
9324     for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9325       mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9326   }
9327
9328   // replace given elements by linear ones
9329   SMDS_ElemIteratorPtr elemIt = SMESHUtils::elemSetIterator( theElements );
9330   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9331
9332   // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9333   // except those elements sharing medium nodes of quadratic element whose medium nodes
9334   // are not all in mediumNodeIDs
9335
9336   // get remaining medium nodes
9337   TIDSortedNodeSet mediumNodes;
9338   set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9339   for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9340     if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9341       mediumNodes.insert( mediumNodes.end(), n );
9342
9343   // find more quadratic elements to convert
9344   TIDSortedElemSet moreElemsToConvert;
9345   TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9346   for ( ; nIt != mediumNodes.end(); ++nIt )
9347   {
9348     SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9349     while ( invIt->more() )
9350     {
9351       const SMDS_MeshElement* e = invIt->next();
9352       if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9353       {
9354         // find a more complex element including e and
9355         // whose medium nodes are not in mediumNodes
9356         bool complexFound = false;
9357         for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9358         {
9359           SMDS_ElemIteratorPtr invIt2 =
9360             (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9361           while ( invIt2->more() )
9362           {
9363             const SMDS_MeshElement* eComplex = invIt2->next();
9364             if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9365             {
9366               int nbCommonNodes = SMESH_MeshAlgos::NbCommonNodes( e, eComplex );
9367               if ( nbCommonNodes == e->NbNodes())
9368               {
9369                 complexFound = true;
9370                 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9371                 break;
9372               }
9373             }
9374           }
9375         }
9376         if ( !complexFound )
9377           moreElemsToConvert.insert( e );
9378       }
9379     }
9380   }
9381   elemIt = SMESHUtils::elemSetIterator( moreElemsToConvert );
9382   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9383 }
9384
9385 //=======================================================================
9386 //function : SewSideElements
9387 //purpose  :
9388 //=======================================================================
9389
9390 SMESH_MeshEditor::Sew_Error
9391 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet&    theSide1,
9392                                    TIDSortedElemSet&    theSide2,
9393                                    const SMDS_MeshNode* theFirstNode1,
9394                                    const SMDS_MeshNode* theFirstNode2,
9395                                    const SMDS_MeshNode* theSecondNode1,
9396                                    const SMDS_MeshNode* theSecondNode2)
9397 {
9398   ClearLastCreated();
9399
9400   if ( theSide1.size() != theSide2.size() )
9401     return SEW_DIFF_NB_OF_ELEMENTS;
9402
9403   Sew_Error aResult = SEW_OK;
9404   // Algo:
9405   // 1. Build set of faces representing each side
9406   // 2. Find which nodes of the side 1 to merge with ones on the side 2
9407   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9408
9409   // =======================================================================
9410   // 1. Build set of faces representing each side:
9411   // =======================================================================
9412   // a. build set of nodes belonging to faces
9413   // b. complete set of faces: find missing faces whose nodes are in set of nodes
9414   // c. create temporary faces representing side of volumes if correspondent
9415   //    face does not exist
9416
9417   SMESHDS_Mesh* aMesh = GetMeshDS();
9418   // TODO algorithm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9419   //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9420   TIDSortedElemSet             faceSet1, faceSet2;
9421   set<const SMDS_MeshElement*> volSet1,  volSet2;
9422   set<const SMDS_MeshNode*>    nodeSet1, nodeSet2;
9423   TIDSortedElemSet             * faceSetPtr[] = { &faceSet1, &faceSet2 };
9424   set<const SMDS_MeshElement*> *  volSetPtr[] = { &volSet1,  &volSet2  };
9425   set<const SMDS_MeshNode*>    * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9426   TIDSortedElemSet             * elemSetPtr[] = { &theSide1, &theSide2 };
9427   int iSide, iFace, iNode;
9428
9429   list<const SMDS_MeshElement* > tempFaceList;
9430   for ( iSide = 0; iSide < 2; iSide++ ) {
9431     set<const SMDS_MeshNode*>    * nodeSet = nodeSetPtr[ iSide ];
9432     TIDSortedElemSet             * elemSet = elemSetPtr[ iSide ];
9433     TIDSortedElemSet             * faceSet = faceSetPtr[ iSide ];
9434     set<const SMDS_MeshElement*> * volSet  = volSetPtr [ iSide ];
9435     set<const SMDS_MeshElement*>::iterator vIt;
9436     TIDSortedElemSet::iterator eIt;
9437     set<const SMDS_MeshNode*>::iterator    nIt;
9438
9439     // check that given nodes belong to given elements
9440     const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9441     const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9442     int firstIndex = -1, secondIndex = -1;
9443     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9444       const SMDS_MeshElement* elem = *eIt;
9445       if ( firstIndex  < 0 ) firstIndex  = elem->GetNodeIndex( n1 );
9446       if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9447       if ( firstIndex > -1 && secondIndex > -1 ) break;
9448     }
9449     if ( firstIndex < 0 || secondIndex < 0 ) {
9450       // we can simply return until temporary faces created
9451       return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9452     }
9453
9454     // -----------------------------------------------------------
9455     // 1a. Collect nodes of existing faces
9456     //     and build set of face nodes in order to detect missing
9457     //     faces corresponding to sides of volumes
9458     // -----------------------------------------------------------
9459
9460     set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9461
9462     // loop on the given element of a side
9463     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9464       //const SMDS_MeshElement* elem = *eIt;
9465       const SMDS_MeshElement* elem = *eIt;
9466       if ( elem->GetType() == SMDSAbs_Face ) {
9467         faceSet->insert( elem );
9468         set <const SMDS_MeshNode*> faceNodeSet;
9469         SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9470         while ( nodeIt->more() ) {
9471           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9472           nodeSet->insert( n );
9473           faceNodeSet.insert( n );
9474         }
9475         setOfFaceNodeSet.insert( faceNodeSet );
9476       }
9477       else if ( elem->GetType() == SMDSAbs_Volume )
9478         volSet->insert( elem );
9479     }
9480     // ------------------------------------------------------------------------------
9481     // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9482     // ------------------------------------------------------------------------------
9483
9484     for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9485       SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9486       while ( fIt->more() ) { // loop on faces sharing a node
9487         const SMDS_MeshElement* f = fIt->next();
9488         if ( faceSet->find( f ) == faceSet->end() ) {
9489           // check if all nodes are in nodeSet and
9490           // complete setOfFaceNodeSet if they are
9491           set <const SMDS_MeshNode*> faceNodeSet;
9492           SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9493           bool allInSet = true;
9494           while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9495             const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9496             if ( nodeSet->find( n ) == nodeSet->end() )
9497               allInSet = false;
9498             else
9499               faceNodeSet.insert( n );
9500           }
9501           if ( allInSet ) {
9502             faceSet->insert( f );
9503             setOfFaceNodeSet.insert( faceNodeSet );
9504           }
9505         }
9506       }
9507     }
9508
9509     // -------------------------------------------------------------------------
9510     // 1c. Create temporary faces representing sides of volumes if correspondent
9511     //     face does not exist
9512     // -------------------------------------------------------------------------
9513
9514     if ( !volSet->empty() ) {
9515       //int nodeSetSize = nodeSet->size();
9516
9517       // loop on given volumes
9518       for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
9519         SMDS_VolumeTool vol (*vIt);
9520         // loop on volume faces: find free faces
9521         // --------------------------------------
9522         list<const SMDS_MeshElement* > freeFaceList;
9523         for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
9524           if ( !vol.IsFreeFace( iFace ))
9525             continue;
9526           // check if there is already a face with same nodes in a face set
9527           const SMDS_MeshElement* aFreeFace = 0;
9528           const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
9529           int nbNodes = vol.NbFaceNodes( iFace );
9530           set <const SMDS_MeshNode*> faceNodeSet;
9531           vol.GetFaceNodes( iFace, faceNodeSet );
9532           bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
9533           if ( isNewFace ) {
9534             // no such a face is given but it still can exist, check it
9535             vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
9536             aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
9537           }
9538           if ( !aFreeFace ) {
9539             // create a temporary face
9540             if ( nbNodes == 3 ) {
9541               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
9542               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
9543             }
9544             else if ( nbNodes == 4 ) {
9545               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9546               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9547             }
9548             else {
9549               vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9550               //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
9551               aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
9552             }
9553             if ( aFreeFace )
9554               tempFaceList.push_back( aFreeFace );
9555           }
9556
9557           if ( aFreeFace )
9558             freeFaceList.push_back( aFreeFace );
9559
9560         } // loop on faces of a volume
9561
9562         // choose one of several free faces of a volume
9563         // --------------------------------------------
9564         if ( freeFaceList.size() > 1 ) {
9565           // choose a face having max nb of nodes shared by other elems of a side
9566           int maxNbNodes = -1;
9567           list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
9568           while ( fIt != freeFaceList.end() ) { // loop on free faces
9569             int nbSharedNodes = 0;
9570             SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9571             while ( nodeIt->more() ) { // loop on free face nodes
9572               const SMDS_MeshNode* n =
9573                 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9574               SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
9575               while ( invElemIt->more() ) {
9576                 const SMDS_MeshElement* e = invElemIt->next();
9577                 nbSharedNodes += faceSet->count( e );
9578                 nbSharedNodes += elemSet->count( e );
9579               }
9580             }
9581             if ( nbSharedNodes > maxNbNodes ) {
9582               maxNbNodes = nbSharedNodes;
9583               freeFaceList.erase( freeFaceList.begin(), fIt++ );
9584             }
9585             else if ( nbSharedNodes == maxNbNodes ) {
9586               fIt++;
9587             }
9588             else {
9589               freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
9590             }
9591           }
9592           if ( freeFaceList.size() > 1 )
9593           {
9594             // could not choose one face, use another way
9595             // choose a face most close to the bary center of the opposite side
9596             gp_XYZ aBC( 0., 0., 0. );
9597             set <const SMDS_MeshNode*> addedNodes;
9598             TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
9599             eIt = elemSet2->begin();
9600             for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
9601               SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
9602               while ( nodeIt->more() ) { // loop on free face nodes
9603                 const SMDS_MeshNode* n =
9604                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9605                 if ( addedNodes.insert( n ).second )
9606                   aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
9607               }
9608             }
9609             aBC /= addedNodes.size();
9610             double minDist = DBL_MAX;
9611             fIt = freeFaceList.begin();
9612             while ( fIt != freeFaceList.end() ) { // loop on free faces
9613               double dist = 0;
9614               SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9615               while ( nodeIt->more() ) { // loop on free face nodes
9616                 const SMDS_MeshNode* n =
9617                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9618                 gp_XYZ p( n->X(),n->Y(),n->Z() );
9619                 dist += ( aBC - p ).SquareModulus();
9620               }
9621               if ( dist < minDist ) {
9622                 minDist = dist;
9623                 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9624               }
9625               else
9626                 fIt = freeFaceList.erase( fIt++ );
9627             }
9628           }
9629         } // choose one of several free faces of a volume
9630
9631         if ( freeFaceList.size() == 1 ) {
9632           const SMDS_MeshElement* aFreeFace = freeFaceList.front();
9633           faceSet->insert( aFreeFace );
9634           // complete a node set with nodes of a found free face
9635           //           for ( iNode = 0; iNode < ; iNode++ )
9636           //             nodeSet->insert( fNodes[ iNode ] );
9637         }
9638
9639       } // loop on volumes of a side
9640
9641       //       // complete a set of faces if new nodes in a nodeSet appeared
9642       //       // ----------------------------------------------------------
9643       //       if ( nodeSetSize != nodeSet->size() ) {
9644       //         for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9645       //           SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9646       //           while ( fIt->more() ) { // loop on faces sharing a node
9647       //             const SMDS_MeshElement* f = fIt->next();
9648       //             if ( faceSet->find( f ) == faceSet->end() ) {
9649       //               // check if all nodes are in nodeSet and
9650       //               // complete setOfFaceNodeSet if they are
9651       //               set <const SMDS_MeshNode*> faceNodeSet;
9652       //               SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9653       //               bool allInSet = true;
9654       //               while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9655       //                 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9656       //                 if ( nodeSet->find( n ) == nodeSet->end() )
9657       //                   allInSet = false;
9658       //                 else
9659       //                   faceNodeSet.insert( n );
9660       //               }
9661       //               if ( allInSet ) {
9662       //                 faceSet->insert( f );
9663       //                 setOfFaceNodeSet.insert( faceNodeSet );
9664       //               }
9665       //             }
9666       //           }
9667       //         }
9668       //       }
9669     } // Create temporary faces, if there are volumes given
9670   } // loop on sides
9671
9672   if ( faceSet1.size() != faceSet2.size() ) {
9673     // delete temporary faces: they are in reverseElements of actual nodes
9674     //    SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9675     //    while ( tmpFaceIt->more() )
9676     //      aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9677     //    list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9678     //    for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9679     //      aMesh->RemoveElement(*tmpFaceIt);
9680     MESSAGE("Diff nb of faces");
9681     return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9682   }
9683
9684   // ============================================================
9685   // 2. Find nodes to merge:
9686   //              bind a node to remove to a node to put instead
9687   // ============================================================
9688
9689   TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
9690   if ( theFirstNode1 != theFirstNode2 )
9691     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9692   if ( theSecondNode1 != theSecondNode2 )
9693     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9694
9695   LinkID_Gen aLinkID_Gen( GetMeshDS() );
9696   set< long > linkIdSet; // links to process
9697   linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
9698
9699   typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
9700   list< NLink > linkList[2];
9701   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9702   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9703   // loop on links in linkList; find faces by links and append links
9704   // of the found faces to linkList
9705   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9706   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
9707   {
9708     NLink link[] = { *linkIt[0], *linkIt[1] };
9709     long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
9710     if ( !linkIdSet.count( linkID ) )
9711       continue;
9712
9713     // by links, find faces in the face sets,
9714     // and find indices of link nodes in the found faces;
9715     // in a face set, there is only one or no face sharing a link
9716     // ---------------------------------------------------------------
9717
9718     const SMDS_MeshElement* face[] = { 0, 0 };
9719     vector<const SMDS_MeshNode*> fnodes[2];
9720     int iLinkNode[2][2];
9721     TIDSortedElemSet avoidSet;
9722     for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9723       const SMDS_MeshNode* n1 = link[iSide].first;
9724       const SMDS_MeshNode* n2 = link[iSide].second;
9725       //cout << "Side " << iSide << " ";
9726       //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
9727       // find a face by two link nodes
9728       face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
9729                                                       *faceSetPtr[ iSide ], avoidSet,
9730                                                       &iLinkNode[iSide][0],
9731                                                       &iLinkNode[iSide][1] );
9732       if ( face[ iSide ])
9733       {
9734         //cout << " F " << face[ iSide]->GetID() <<endl;
9735         faceSetPtr[ iSide ]->erase( face[ iSide ]);
9736         // put face nodes to fnodes
9737         SMDS_MeshElement::iterator nIt( face[ iSide ]->interlacedNodesIterator() ), nEnd;
9738         fnodes[ iSide ].assign( nIt, nEnd );
9739         fnodes[ iSide ].push_back( fnodes[ iSide ].front());
9740       }
9741     }
9742
9743     // check similarity of elements of the sides
9744     if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
9745       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
9746       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
9747         aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9748       }
9749       else {
9750         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9751       }
9752       break; // do not return because it's necessary to remove tmp faces
9753     }
9754
9755     // set nodes to merge
9756     // -------------------
9757
9758     if ( face[0] && face[1] )  {
9759       const int nbNodes = face[0]->NbNodes();
9760       if ( nbNodes != face[1]->NbNodes() ) {
9761         MESSAGE("Diff nb of face nodes");
9762         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9763         break; // do not return because it s necessary to remove tmp faces
9764       }
9765       bool reverse[] = { false, false }; // order of nodes in the link
9766       for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9767         // analyse link orientation in faces
9768         int i1 = iLinkNode[ iSide ][ 0 ];
9769         int i2 = iLinkNode[ iSide ][ 1 ];
9770         reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
9771       }
9772       int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
9773       int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
9774       for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
9775       {
9776         nReplaceMap.insert  ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
9777                                           fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
9778       }
9779
9780       // add other links of the faces to linkList
9781       // -----------------------------------------
9782
9783       for ( iNode = 0; iNode < nbNodes; iNode++ )  {
9784         linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
9785         pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
9786         if ( !iter_isnew.second ) { // already in a set: no need to process
9787           linkIdSet.erase( iter_isnew.first );
9788         }
9789         else // new in set == encountered for the first time: add
9790         {
9791           const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
9792           const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
9793           linkList[0].push_back ( NLink( n1, n2 ));
9794           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
9795         }
9796       }
9797     } // 2 faces found
9798
9799     if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
9800       break;
9801
9802   } // loop on link lists
9803
9804   if ( aResult == SEW_OK &&
9805        ( //linkIt[0] != linkList[0].end() ||
9806         !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
9807     MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
9808              " " << (faceSetPtr[1]->empty()));
9809     aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9810   }
9811
9812   // ====================================================================
9813   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9814   // ====================================================================
9815
9816   // delete temporary faces
9817   //  SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9818   //  while ( tmpFaceIt->more() )
9819   //    aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9820   list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9821   for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9822     aMesh->RemoveElement(*tmpFaceIt);
9823
9824   if ( aResult != SEW_OK)
9825     return aResult;
9826
9827   list< int > nodeIDsToRemove;
9828   vector< const SMDS_MeshNode*> nodes;
9829   ElemFeatures elemType;
9830
9831   // loop on nodes replacement map
9832   TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
9833   for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
9834     if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second )
9835     {
9836       const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
9837       nodeIDsToRemove.push_back( nToRemove->GetID() );
9838       // loop on elements sharing nToRemove
9839       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
9840       while ( invElemIt->more() ) {
9841         const SMDS_MeshElement* e = invElemIt->next();
9842         // get a new suite of nodes: make replacement
9843         int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
9844         nodes.resize( nbNodes );
9845         SMDS_ElemIteratorPtr nIt = e->nodesIterator();
9846         while ( nIt->more() ) {
9847           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
9848           nnIt = nReplaceMap.find( n );
9849           if ( nnIt != nReplaceMap.end() ) {
9850             nbReplaced++;
9851             n = (*nnIt).second;
9852           }
9853           nodes[ i++ ] = n;
9854         }
9855         //       if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
9856         //         elemIDsToRemove.push_back( e->GetID() );
9857         //       else
9858         if ( nbReplaced )
9859         {
9860           elemType.Init( e, /*basicOnly=*/false ).SetID( e->GetID() );
9861           aMesh->RemoveElement( e );
9862
9863           if ( SMDS_MeshElement* newElem = this->AddElement( nodes, elemType ))
9864           {
9865             AddToSameGroups( newElem, e, aMesh );
9866             if ( int aShapeId = e->getshapeId() )
9867               aMesh->SetMeshElementOnShape( newElem, aShapeId );
9868           }
9869         }
9870       }
9871     }
9872
9873   Remove( nodeIDsToRemove, true );
9874
9875   return aResult;
9876 }
9877
9878 //================================================================================
9879 /*!
9880  * \brief Find corresponding nodes in two sets of faces
9881  * \param theSide1 - first face set
9882  * \param theSide2 - second first face
9883  * \param theFirstNode1 - a boundary node of set 1
9884  * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
9885  * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
9886  * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
9887  * \param nReplaceMap - output map of corresponding nodes
9888  * \return bool  - is a success or not
9889  */
9890 //================================================================================
9891
9892 #ifdef _DEBUG_
9893 //#define DEBUG_MATCHING_NODES
9894 #endif
9895
9896 SMESH_MeshEditor::Sew_Error
9897 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
9898                                     set<const SMDS_MeshElement*>& theSide2,
9899                                     const SMDS_MeshNode*          theFirstNode1,
9900                                     const SMDS_MeshNode*          theFirstNode2,
9901                                     const SMDS_MeshNode*          theSecondNode1,
9902                                     const SMDS_MeshNode*          theSecondNode2,
9903                                     TNodeNodeMap &                nReplaceMap)
9904 {
9905   set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
9906
9907   nReplaceMap.clear();
9908   //if ( theFirstNode1 != theFirstNode2 )
9909   nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9910   //if ( theSecondNode1 != theSecondNode2 )
9911   nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9912
9913   set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
9914   linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
9915
9916   list< NLink > linkList[2];
9917   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9918   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9919
9920   // loop on links in linkList; find faces by links and append links
9921   // of the found faces to linkList
9922   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9923   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
9924     NLink link[] = { *linkIt[0], *linkIt[1] };
9925     if ( linkSet.find( link[0] ) == linkSet.end() )
9926       continue;
9927
9928     // by links, find faces in the face sets,
9929     // and find indices of link nodes in the found faces;
9930     // in a face set, there is only one or no face sharing a link
9931     // ---------------------------------------------------------------
9932
9933     const SMDS_MeshElement* face[] = { 0, 0 };
9934     list<const SMDS_MeshNode*> notLinkNodes[2];
9935     //bool reverse[] = { false, false }; // order of notLinkNodes
9936     int nbNodes[2];
9937     for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
9938     {
9939       const SMDS_MeshNode* n1 = link[iSide].first;
9940       const SMDS_MeshNode* n2 = link[iSide].second;
9941       set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
9942       set< const SMDS_MeshElement* > facesOfNode1;
9943       for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
9944       {
9945         // during a loop of the first node, we find all faces around n1,
9946         // during a loop of the second node, we find one face sharing both n1 and n2
9947         const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
9948         SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
9949         while ( fIt->more() ) { // loop on faces sharing a node
9950           const SMDS_MeshElement* f = fIt->next();
9951           if (faceSet->find( f ) != faceSet->end() && // f is in face set
9952               ! facesOfNode1.insert( f ).second ) // f encounters twice
9953           {
9954             if ( face[ iSide ] ) {
9955               MESSAGE( "2 faces per link " );
9956               return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9957             }
9958             face[ iSide ] = f;
9959             faceSet->erase( f );
9960
9961             // get not link nodes
9962             int nbN = f->NbNodes();
9963             if ( f->IsQuadratic() )
9964               nbN /= 2;
9965             nbNodes[ iSide ] = nbN;
9966             list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
9967             int i1 = f->GetNodeIndex( n1 );
9968             int i2 = f->GetNodeIndex( n2 );
9969             int iEnd = nbN, iBeg = -1, iDelta = 1;
9970             bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
9971             if ( reverse ) {
9972               std::swap( iEnd, iBeg ); iDelta = -1;
9973             }
9974             int i = i2;
9975             while ( true ) {
9976               i += iDelta;
9977               if ( i == iEnd ) i = iBeg + iDelta;
9978               if ( i == i1 ) break;
9979               nodes.push_back ( f->GetNode( i ) );
9980             }
9981           }
9982         }
9983       }
9984     }
9985     // check similarity of elements of the sides
9986     if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
9987       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
9988       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
9989         return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9990       }
9991       else {
9992         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9993       }
9994     }
9995
9996     // set nodes to merge
9997     // -------------------
9998
9999     if ( face[0] && face[1] )  {
10000       if ( nbNodes[0] != nbNodes[1] ) {
10001         MESSAGE("Diff nb of face nodes");
10002         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10003       }
10004 #ifdef DEBUG_MATCHING_NODES
10005       MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10006                 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10007                 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10008 #endif
10009       int nbN = nbNodes[0];
10010       {
10011         list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10012         list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10013         for ( int i = 0 ; i < nbN - 2; ++i ) {
10014 #ifdef DEBUG_MATCHING_NODES
10015           MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10016 #endif
10017           nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10018         }
10019       }
10020
10021       // add other links of the face 1 to linkList
10022       // -----------------------------------------
10023
10024       const SMDS_MeshElement* f0 = face[0];
10025       const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10026       for ( int i = 0; i < nbN; i++ )
10027       {
10028         const SMDS_MeshNode* n2 = f0->GetNode( i );
10029         pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10030           linkSet.insert( SMESH_TLink( n1, n2 ));
10031         if ( !iter_isnew.second ) { // already in a set: no need to process
10032           linkSet.erase( iter_isnew.first );
10033         }
10034         else // new in set == encountered for the first time: add
10035         {
10036 #ifdef DEBUG_MATCHING_NODES
10037           MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10038                     << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10039 #endif
10040           linkList[0].push_back ( NLink( n1, n2 ));
10041           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10042         }
10043         n1 = n2;
10044       }
10045     } // 2 faces found
10046   } // loop on link lists
10047
10048   return SEW_OK;
10049 }
10050
10051 namespace // automatically find theAffectedElems for DoubleNodes()
10052 {
10053   bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem );
10054
10055   //--------------------------------------------------------------------------------
10056   // Nodes shared by adjacent FissureBorder's.
10057   // 1 node  if FissureBorder separates faces
10058   // 2 nodes if FissureBorder separates volumes
10059   struct SubBorder
10060   {
10061     const SMDS_MeshNode* _nodes[2];
10062     int                  _nbNodes;
10063
10064     SubBorder( const SMDS_MeshNode* n1, const SMDS_MeshNode* n2 = 0 )
10065     {
10066       _nodes[0] = n1;
10067       _nodes[1] = n2;
10068       _nbNodes = bool( n1 ) + bool( n2 );
10069       if ( _nbNodes == 2 && n1 > n2 )
10070         std::swap( _nodes[0], _nodes[1] );
10071     }
10072     bool operator<( const SubBorder& other ) const
10073     {
10074       for ( int i = 0; i < _nbNodes; ++i )
10075       {
10076         if ( _nodes[i] < other._nodes[i] ) return true;
10077         if ( _nodes[i] > other._nodes[i] ) return false;
10078       }
10079       return false;
10080     }
10081   };
10082
10083   //--------------------------------------------------------------------------------
10084   // Map a SubBorder to all FissureBorder it bounds
10085   struct FissureBorder;
10086   typedef std::map< SubBorder, std::vector< FissureBorder* > > TBorderLinks;
10087   typedef TBorderLinks::iterator                               TMappedSub;
10088
10089   //--------------------------------------------------------------------------------
10090   /*!
10091    * \brief Element border (volume facet or face edge) at a fissure
10092    */
10093   struct FissureBorder
10094   {
10095     std::vector< const SMDS_MeshNode* > _nodes;    // border nodes
10096     const SMDS_MeshElement*             _elems[2]; // volume or face adjacent to fissure
10097
10098     std::vector< TMappedSub           > _mappedSubs;  // Sub() in TBorderLinks map
10099     std::vector< const SMDS_MeshNode* > _sortedNodes; // to compare FissureBorder's
10100
10101     FissureBorder( FissureBorder && from ) // move constructor
10102     {
10103       std::swap( _nodes,       from._nodes );
10104       std::swap( _sortedNodes, from._sortedNodes );
10105       _elems[0] = from._elems[0];
10106       _elems[1] = from._elems[1];
10107     }
10108
10109     FissureBorder( const SMDS_MeshElement*                  elemToDuplicate,
10110                    std::vector< const SMDS_MeshElement* > & adjElems)
10111       : _nodes( elemToDuplicate->NbCornerNodes() )
10112     {
10113       for ( size_t i = 0; i < _nodes.size(); ++i )
10114         _nodes[i] = elemToDuplicate->GetNode( i );
10115
10116       SMDSAbs_ElementType type = SMDSAbs_ElementType( elemToDuplicate->GetType() + 1 );
10117       findAdjacent( type, adjElems );
10118     }
10119
10120     FissureBorder( const SMDS_MeshNode**                    nodes,
10121                    const size_t                             nbNodes,
10122                    const SMDSAbs_ElementType                adjElemsType,
10123                    std::vector< const SMDS_MeshElement* > & adjElems)
10124       : _nodes( nodes, nodes + nbNodes )
10125     {
10126       findAdjacent( adjElemsType, adjElems );
10127     }
10128
10129     void findAdjacent( const SMDSAbs_ElementType                adjElemsType,
10130                        std::vector< const SMDS_MeshElement* > & adjElems)
10131     {
10132       _elems[0] = _elems[1] = 0;
10133       adjElems.clear();
10134       if ( SMDS_Mesh::GetElementsByNodes( _nodes, adjElems, adjElemsType ))
10135         for ( size_t i = 0; i < adjElems.size() && i < 2; ++i )
10136           _elems[i] = adjElems[i];
10137     }
10138
10139     bool operator<( const FissureBorder& other ) const
10140     {
10141       return GetSortedNodes() < other.GetSortedNodes();
10142     }
10143
10144     const std::vector< const SMDS_MeshNode* >& GetSortedNodes() const
10145     {
10146       if ( _sortedNodes.empty() && !_nodes.empty() )
10147       {
10148         FissureBorder* me = const_cast<FissureBorder*>( this );
10149         me->_sortedNodes = me->_nodes;
10150         std::sort( me->_sortedNodes.begin(), me->_sortedNodes.end() );
10151       }
10152       return _sortedNodes;
10153     }
10154
10155     size_t NbSub() const
10156     {
10157       return _nodes.size();
10158     }
10159
10160     SubBorder Sub(size_t i) const
10161     {
10162       return SubBorder( _nodes[i], NbSub() > 2 ? _nodes[ (i+1)%NbSub() ] : 0 );
10163     }
10164
10165     void AddSelfTo( TBorderLinks& borderLinks )
10166     {
10167       _mappedSubs.resize( NbSub() );
10168       for ( size_t i = 0; i < NbSub(); ++i )
10169       {
10170         TBorderLinks::iterator s2b =
10171           borderLinks.insert( std::make_pair( Sub(i), TBorderLinks::mapped_type() )).first;
10172         s2b->second.push_back( this );
10173         _mappedSubs[ i ] = s2b;
10174       }
10175     }
10176
10177     void Clear()
10178     {
10179       _nodes.clear();
10180     }
10181
10182     const SMDS_MeshElement* GetMarkedElem() const
10183     {
10184       if ( _nodes.empty() ) return 0; // cleared
10185       if ( _elems[0] && _elems[0]->isMarked() ) return _elems[0];
10186       if ( _elems[1] && _elems[1]->isMarked() ) return _elems[1];
10187       return 0;
10188     }
10189
10190     gp_XYZ GetNorm() const // normal to the border
10191     {
10192       gp_XYZ norm;
10193       if ( _nodes.size() == 2 )
10194       {
10195         gp_XYZ avgNorm( 0,0,0 ); // sum of normals of adjacent faces
10196         if ( SMESH_MeshAlgos::FaceNormal( _elems[0], norm ))
10197           avgNorm += norm;
10198         if ( SMESH_MeshAlgos::FaceNormal( _elems[1], norm ))
10199           avgNorm += norm;
10200
10201         gp_XYZ bordDir( SMESH_NodeXYZ( _nodes[0] ) - SMESH_NodeXYZ( _nodes[1] ));
10202         norm = bordDir ^ avgNorm;
10203       }
10204       else
10205       {
10206         SMESH_NodeXYZ p0( _nodes[0] );
10207         SMESH_NodeXYZ p1( _nodes[1] );
10208         SMESH_NodeXYZ p2( _nodes[2] );
10209         norm = ( p0 - p1 ) ^ ( p2 - p1 );
10210       }
10211       if ( isOut( _nodes[0], norm, GetMarkedElem() ))
10212         norm.Reverse();
10213
10214       return norm;
10215     }
10216
10217     void ChooseSide() // mark an _elem located at positive side of fissure
10218     {
10219       _elems[0]->setIsMarked( true );
10220       gp_XYZ norm = GetNorm();
10221       double maxX = norm.Coord(1);
10222       if ( Abs( maxX ) < Abs( norm.Coord(2)) ) maxX = norm.Coord(2);
10223       if ( Abs( maxX ) < Abs( norm.Coord(3)) ) maxX = norm.Coord(3);
10224       if ( maxX < 0 )
10225       {
10226         _elems[0]->setIsMarked( false );
10227         _elems[1]->setIsMarked( true );
10228       }
10229     }
10230
10231   }; // struct FissureBorder
10232
10233   //--------------------------------------------------------------------------------
10234   /*!
10235    * \brief Classifier of elements at fissure edge
10236    */
10237   class FissureNormal
10238   {
10239     std::vector< gp_XYZ > _normals;
10240     bool                  _bothIn;
10241
10242   public:
10243     void Add( const SMDS_MeshNode* n, const FissureBorder& bord )
10244     {
10245       _bothIn = false;
10246       _normals.reserve(2);
10247       _normals.push_back( bord.GetNorm() );
10248       if ( _normals.size() == 2 )
10249         _bothIn = !isOut( n, _normals[0], bord.GetMarkedElem() );
10250     }
10251
10252     bool IsIn( const SMDS_MeshNode* n, const SMDS_MeshElement* elem ) const
10253     {
10254       bool isIn = false;
10255       switch ( _normals.size() ) {
10256       case 1:
10257       {
10258         isIn = !isOut( n, _normals[0], elem );
10259         break;
10260       }
10261       case 2:
10262       {
10263         bool in1 = !isOut( n, _normals[0], elem );
10264         bool in2 = !isOut( n, _normals[1], elem );
10265         isIn = _bothIn ? ( in1 && in2 ) : ( in1 || in2 );
10266       }
10267       }
10268       return isIn;
10269     }
10270   };
10271
10272   //================================================================================
10273   /*!
10274    * \brief Classify an element by a plane passing through a node
10275    */
10276   //================================================================================
10277
10278   bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem )
10279   {
10280     SMESH_NodeXYZ p = n;
10281     double sumDot = 0;
10282     for ( int i = 0, nb = elem->NbCornerNodes(); i < nb; ++i )
10283     {
10284       SMESH_NodeXYZ pi = elem->GetNode( i );
10285       sumDot += norm * ( pi - p );
10286     }
10287     return sumDot < -1e-100;
10288   }
10289
10290   //================================================================================
10291   /*!
10292    * \brief Find FissureBorder's by nodes to duplicate
10293    */
10294   //================================================================================
10295
10296   void findFissureBorders( const TIDSortedElemSet&        theNodes,
10297                            std::vector< FissureBorder > & theFissureBorders )
10298   {
10299     TIDSortedElemSet::const_iterator nIt = theNodes.begin();
10300     const SMDS_MeshNode* n = dynamic_cast< const SMDS_MeshNode*>( *nIt );
10301     if ( !n ) return;
10302     SMDSAbs_ElementType elemType = SMDSAbs_Volume;
10303     if ( n->NbInverseElements( elemType ) == 0 )
10304     {
10305       elemType = SMDSAbs_Face;
10306       if ( n->NbInverseElements( elemType ) == 0 )
10307         return;
10308     }
10309     // unmark elements touching the fissure
10310     for ( ; nIt != theNodes.end(); ++nIt )
10311       SMESH_MeshAlgos::MarkElems( cast2Node(*nIt)->GetInverseElementIterator(), false );
10312
10313     // loop on elements touching the fissure to get their borders belonging to the fissure
10314     std::set< FissureBorder >              fissureBorders;
10315     std::vector< const SMDS_MeshElement* > adjElems;
10316     std::vector< const SMDS_MeshNode* >    nodes;
10317     SMDS_VolumeTool volTool;
10318     for ( nIt = theNodes.begin(); nIt != theNodes.end(); ++nIt )
10319     {
10320       SMDS_ElemIteratorPtr invIt = cast2Node(*nIt)->GetInverseElementIterator( elemType );
10321       while ( invIt->more() )
10322       {
10323         const SMDS_MeshElement* eInv = invIt->next();
10324         if ( eInv->isMarked() ) continue;
10325         eInv->setIsMarked( true );
10326
10327         if ( elemType == SMDSAbs_Volume )
10328         {
10329           volTool.Set( eInv );
10330           int iQuad = eInv->IsQuadratic() ? 2 : 1;
10331           for ( int iF = 0, nbF = volTool.NbFaces(); iF < nbF; ++iF )
10332           {
10333             const SMDS_MeshNode** nn = volTool.GetFaceNodes( iF );
10334             int                  nbN = volTool.NbFaceNodes( iF ) / iQuad;
10335             nodes.clear();
10336             bool allOnFissure = true;
10337             for ( int iN = 0; iN < nbN  && allOnFissure; iN += iQuad )
10338               if (( allOnFissure = theNodes.count( nn[ iN ])))
10339                 nodes.push_back( nn[ iN ]);
10340             if ( allOnFissure )
10341               fissureBorders.insert( std::move( FissureBorder( &nodes[0], nodes.size(),
10342                                                                elemType, adjElems )));
10343           }
10344         }
10345         else // elemType == SMDSAbs_Face
10346         {
10347           const SMDS_MeshNode* nn[2] = { eInv->GetNode( eInv->NbCornerNodes()-1 ), 0 };
10348           bool            onFissure0 = theNodes.count( nn[0] ), onFissure1;
10349           for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN; ++iN )
10350           {
10351             nn[1]      = eInv->GetNode( iN );
10352             onFissure1 = theNodes.count( nn[1] );
10353             if ( onFissure0 && onFissure1 )
10354               fissureBorders.insert( std::move( FissureBorder( nn, 2, elemType, adjElems )));
10355             nn[0]      = nn[1];
10356             onFissure0 = onFissure1;
10357           }
10358         }
10359       }
10360     }
10361
10362     theFissureBorders.reserve( theFissureBorders.size() + fissureBorders.size());
10363     std::set< FissureBorder >::iterator bord = fissureBorders.begin();
10364     for ( ; bord != fissureBorders.end(); ++bord )
10365     {
10366       theFissureBorders.push_back( std::move( const_cast<FissureBorder&>( *bord ) ));
10367     }
10368     return;
10369   } // findFissureBorders()
10370
10371   //================================================================================
10372   /*!
10373    * \brief Find elements on one side of a fissure defined by elements or nodes to duplicate
10374    *  \param [in] theElemsOrNodes - elements or nodes to duplicate
10375    *  \param [in] theNodesNot - nodes not to duplicate
10376    *  \param [out] theAffectedElems - the found elements
10377    */
10378   //================================================================================
10379
10380   void findAffectedElems( const TIDSortedElemSet& theElemsOrNodes,
10381                           TIDSortedElemSet&       theAffectedElems)
10382   {
10383     if ( theElemsOrNodes.empty() ) return;
10384
10385     // find FissureBorder's
10386
10387     std::vector< FissureBorder >           fissure;
10388     std::vector< const SMDS_MeshElement* > elemsByFacet;
10389
10390     TIDSortedElemSet::const_iterator elIt = theElemsOrNodes.begin();
10391     if ( (*elIt)->GetType() == SMDSAbs_Node )
10392     {
10393       findFissureBorders( theElemsOrNodes, fissure );
10394     }
10395     else
10396     {
10397       fissure.reserve( theElemsOrNodes.size() );
10398       for ( ; elIt != theElemsOrNodes.end(); ++elIt )
10399         fissure.push_back( std::move( FissureBorder( *elIt, elemsByFacet )));
10400     }
10401     if ( fissure.empty() )
10402       return;
10403
10404     // fill borderLinks
10405
10406     TBorderLinks borderLinks;
10407
10408     for ( size_t i = 0; i < fissure.size(); ++i )
10409     {
10410       fissure[i].AddSelfTo( borderLinks );
10411     }
10412
10413     // get theAffectedElems
10414
10415     // unmark elements having nodes on the fissure, theAffectedElems elements will be marked
10416     for ( size_t i = 0; i < fissure.size(); ++i )
10417       for ( size_t j = 0; j < fissure[i]._nodes.size(); ++j )
10418       {
10419         SMESH_MeshAlgos::MarkElemNodes( fissure[i]._nodes[j]->GetInverseElementIterator(),
10420                                         false, /*markElem=*/true );
10421       }
10422
10423     std::vector<const SMDS_MeshNode *>                 facetNodes;
10424     std::map< const SMDS_MeshNode*, FissureNormal >    fissEdgeNodes2Norm;
10425     boost::container::flat_set< const SMDS_MeshNode* > fissureNodes;
10426
10427     // choose a side of fissure
10428     fissure[0].ChooseSide();
10429     theAffectedElems.insert( fissure[0].GetMarkedElem() );
10430
10431     size_t nbCheckedBorders = 0;
10432     while ( nbCheckedBorders < fissure.size() )
10433     {
10434       // find a FissureBorder to treat
10435       FissureBorder* bord = 0;
10436       for ( size_t i = 0; i < fissure.size()  && !bord; ++i )
10437         if ( fissure[i].GetMarkedElem() )
10438           bord = & fissure[i];
10439       for ( size_t i = 0; i < fissure.size()  && !bord; ++i )
10440         if ( fissure[i].NbSub() > 0 && fissure[i]._elems[0] )
10441         {
10442           bord = & fissure[i];
10443           bord->ChooseSide();
10444           theAffectedElems.insert( bord->GetMarkedElem() );
10445         }
10446       if ( !bord ) return;
10447       ++nbCheckedBorders;
10448
10449       // treat FissureBorder's linked to bord
10450       fissureNodes.clear();
10451       fissureNodes.insert( bord->_nodes.begin(), bord->_nodes.end() );
10452       for ( size_t i = 0; i < bord->NbSub(); ++i )
10453       {
10454         TBorderLinks::iterator l2b = bord->_mappedSubs[ i ];
10455         if ( l2b == borderLinks.end() || l2b->second.empty() ) continue;
10456         std::vector< FissureBorder* >& linkedBorders = l2b->second;
10457         const SubBorder&                          sb = l2b->first;
10458         const SMDS_MeshElement*             bordElem = bord->GetMarkedElem();
10459
10460         if ( linkedBorders.size() == 1 ) // fissure edge reached, fill fissEdgeNodes2Norm
10461         {
10462           for ( int j = 0; j < sb._nbNodes; ++j )
10463             fissEdgeNodes2Norm[ sb._nodes[j] ].Add( sb._nodes[j], *bord );
10464           continue;
10465         }
10466
10467         // add to theAffectedElems elems sharing nodes of a SubBorder and a node of bordElem
10468         // until an elem adjacent to a neighbour FissureBorder is found
10469         facetNodes.clear();
10470         facetNodes.insert( facetNodes.end(), sb._nodes, sb._nodes + sb._nbNodes );
10471         facetNodes.resize( sb._nbNodes + 1 );
10472
10473         while ( bordElem )
10474         {
10475           // check if bordElem is adjacent to a neighbour FissureBorder
10476           for ( size_t j = 0; j < linkedBorders.size(); ++j )
10477           {
10478             FissureBorder* bord2 = linkedBorders[j];
10479             if ( bord2 == bord ) continue;
10480             if ( bordElem == bord2->_elems[0] || bordElem == bord2->_elems[1] )
10481               bordElem = 0;
10482             else
10483               fissureNodes.insert( bord2->_nodes.begin(), bord2->_nodes.end() );
10484           }
10485           if ( !bordElem )
10486             break;
10487
10488           // find the next bordElem
10489           const SMDS_MeshElement* nextBordElem = 0;
10490           for ( int iN = 0, nbN = bordElem->NbCornerNodes(); iN < nbN  && !nextBordElem; ++iN )
10491           {
10492             const SMDS_MeshNode* n = bordElem->GetNode( iN );
10493             if ( fissureNodes.count( n )) continue;
10494
10495             facetNodes[ sb._nbNodes ] = n;
10496             elemsByFacet.clear();
10497             if ( SMDS_Mesh::GetElementsByNodes( facetNodes, elemsByFacet ) > 1 )
10498             {
10499               for ( size_t iE = 0; iE < elemsByFacet.size(); ++iE )
10500                 if ( elemsByFacet[ iE ] != bordElem &&
10501                      !elemsByFacet[ iE ]->isMarked() )
10502                 {
10503                   theAffectedElems.insert( elemsByFacet[ iE ]);
10504                   elemsByFacet[ iE ]->setIsMarked( true );
10505                   if ( elemsByFacet[ iE ]->GetType() == bordElem->GetType() )
10506                     nextBordElem = elemsByFacet[ iE ];
10507                 }
10508             }
10509           }
10510           bordElem = nextBordElem;
10511
10512         } // while ( bordElem )
10513
10514         linkedBorders.clear(); // not to treat this link any more
10515
10516       } // loop on SubBorder's of a FissureBorder
10517
10518       bord->Clear();
10519
10520     } // loop on FissureBorder's
10521
10522
10523     // add elements sharing only one node of the fissure, except those sharing fissure edge nodes
10524
10525     // mark nodes of theAffectedElems
10526     SMESH_MeshAlgos::MarkElemNodes( theAffectedElems.begin(), theAffectedElems.end(), true );
10527
10528     // unmark nodes of the fissure
10529     elIt = theElemsOrNodes.begin();
10530     if ( (*elIt)->GetType() == SMDSAbs_Node )
10531       SMESH_MeshAlgos::MarkElems( elIt, theElemsOrNodes.end(), false );
10532     else
10533       SMESH_MeshAlgos::MarkElemNodes( elIt, theElemsOrNodes.end(), false );
10534
10535     std::vector< gp_XYZ > normVec;
10536
10537     // loop on nodes of the fissure, add elements having marked nodes
10538     for ( elIt = theElemsOrNodes.begin(); elIt != theElemsOrNodes.end(); ++elIt )
10539     {
10540       const SMDS_MeshElement* e = (*elIt);
10541       if ( e->GetType() != SMDSAbs_Node )
10542         e->setIsMarked( true ); // avoid adding a fissure element
10543
10544       for ( int iN = 0, nbN = e->NbCornerNodes(); iN < nbN; ++iN )
10545       {
10546         const SMDS_MeshNode* n = e->GetNode( iN );
10547         if ( fissEdgeNodes2Norm.count( n ))
10548           continue;
10549
10550         SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
10551         while ( invIt->more() )
10552         {
10553           const SMDS_MeshElement* eInv = invIt->next();
10554           if ( eInv->isMarked() ) continue;
10555           eInv->setIsMarked( true );
10556
10557           SMDS_ElemIteratorPtr nIt = eInv->nodesIterator();
10558           while( nIt->more() )
10559             if ( nIt->next()->isMarked())
10560             {
10561               theAffectedElems.insert( eInv );
10562               SMESH_MeshAlgos::MarkElems( eInv->nodesIterator(), true );
10563               n->setIsMarked( false );
10564               break;
10565             }
10566         }
10567       }
10568     }
10569
10570     // add elements on the fissure edge
10571     std::map< const SMDS_MeshNode*, FissureNormal >::iterator n2N;
10572     for ( n2N = fissEdgeNodes2Norm.begin(); n2N != fissEdgeNodes2Norm.end(); ++n2N )
10573     {
10574       const SMDS_MeshNode* edgeNode = n2N->first;
10575       const FissureNormal & normals = n2N->second;
10576
10577       SMDS_ElemIteratorPtr invIt = edgeNode->GetInverseElementIterator();
10578       while ( invIt->more() )
10579       {
10580         const SMDS_MeshElement* eInv = invIt->next();
10581         if ( eInv->isMarked() ) continue;
10582         eInv->setIsMarked( true );
10583
10584         // classify eInv using normals
10585         bool toAdd = normals.IsIn( edgeNode, eInv );
10586         if ( toAdd ) // check if all nodes lie on the fissure edge
10587         {
10588           bool notOnEdge = false;
10589           for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN  && !notOnEdge; ++iN )
10590             notOnEdge = !fissEdgeNodes2Norm.count( eInv->GetNode( iN ));
10591           toAdd = notOnEdge;
10592         }
10593         if ( toAdd )
10594         {
10595           theAffectedElems.insert( eInv );
10596         }
10597       }
10598     }
10599
10600     return;
10601   } // findAffectedElems()
10602 } // namespace
10603
10604 //================================================================================
10605 /*!
10606  * \brief Create elements equal (on same nodes) to given ones
10607  *  \param [in] theElements - a set of elems to duplicate. If it is empty, all
10608  *              elements of the uppest dimension are duplicated.
10609  */
10610 //================================================================================
10611
10612 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
10613 {
10614   ClearLastCreated();
10615   SMESHDS_Mesh* mesh = GetMeshDS();
10616
10617   // get an element type and an iterator over elements
10618
10619   SMDSAbs_ElementType type = SMDSAbs_All;
10620   SMDS_ElemIteratorPtr elemIt;
10621   if ( theElements.empty() )
10622   {
10623     if ( mesh->NbNodes() == 0 )
10624       return;
10625     // get most complex type
10626     SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10627       SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10628       SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10629     };
10630     for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10631       if ( mesh->GetMeshInfo().NbElements( types[i] ))
10632       {
10633         type = types[i];
10634         elemIt = mesh->elementsIterator( type );
10635         break;
10636       }
10637   }
10638   else
10639   {
10640     //type = (*theElements.begin())->GetType();
10641     elemIt = SMESHUtils::elemSetIterator( theElements );
10642   }
10643
10644   // un-mark all elements to avoid duplicating just created elements
10645   SMESH_MeshAlgos::MarkElems( mesh->elementsIterator( type ), false );
10646
10647   // duplicate elements
10648
10649   ElemFeatures elemType;
10650
10651   vector< const SMDS_MeshNode* > nodes;
10652   while ( elemIt->more() )
10653   {
10654     const SMDS_MeshElement* elem = elemIt->next();
10655     if (( type != SMDSAbs_All && elem->GetType() != type ) ||
10656         ( elem->isMarked() ))
10657       continue;
10658
10659     elemType.Init( elem, /*basicOnly=*/false );
10660     nodes.assign( elem->begin_nodes(), elem->end_nodes() );
10661
10662     if ( const SMDS_MeshElement* newElem = AddElement( nodes, elemType ))
10663       newElem->setIsMarked( true );
10664   }
10665 }
10666
10667 //================================================================================
10668 /*!
10669   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10670   \param theElems - the list of elements (edges or faces) to be replicated
10671   The nodes for duplication could be found from these elements
10672   \param theNodesNot - list of nodes to NOT replicate
10673   \param theAffectedElems - the list of elements (cells and edges) to which the
10674   replicated nodes should be associated to.
10675   \return TRUE if operation has been completed successfully, FALSE otherwise
10676 */
10677 //================================================================================
10678
10679 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10680                                     const TIDSortedElemSet& theNodesNot,
10681                                     const TIDSortedElemSet& theAffectedElems )
10682 {
10683   ClearLastCreated();
10684
10685   if ( theElems.size() == 0 )
10686     return false;
10687
10688   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10689   if ( !aMeshDS )
10690     return false;
10691
10692   bool res = false;
10693   TNodeNodeMap anOldNodeToNewNode;
10694   // duplicate elements and nodes
10695   res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10696   // replce nodes by duplications
10697   res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10698   return res;
10699 }
10700
10701 //================================================================================
10702 /*!
10703   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10704   \param theMeshDS - mesh instance
10705   \param theElems - the elements replicated or modified (nodes should be changed)
10706   \param theNodesNot - nodes to NOT replicate
10707   \param theNodeNodeMap - relation of old node to new created node
10708   \param theIsDoubleElem - flag os to replicate element or modify
10709   \return TRUE if operation has been completed successfully, FALSE otherwise
10710 */
10711 //================================================================================
10712
10713 bool SMESH_MeshEditor::doubleNodes(SMESHDS_Mesh*           theMeshDS,
10714                                    const TIDSortedElemSet& theElems,
10715                                    const TIDSortedElemSet& theNodesNot,
10716                                    TNodeNodeMap&           theNodeNodeMap,
10717                                    const bool              theIsDoubleElem )
10718 {
10719   // iterate through element and duplicate them (by nodes duplication)
10720   bool res = false;
10721   std::vector<const SMDS_MeshNode*> newNodes;
10722   ElemFeatures elemType;
10723
10724   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10725   for ( ;  elemItr != theElems.end(); ++elemItr )
10726   {
10727     const SMDS_MeshElement* anElem = *elemItr;
10728     // if (!anElem)
10729     //   continue;
10730
10731     // duplicate nodes to duplicate element
10732     bool isDuplicate = false;
10733     newNodes.resize( anElem->NbNodes() );
10734     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10735     int ind = 0;
10736     while ( anIter->more() )
10737     {
10738       const SMDS_MeshNode* aCurrNode = static_cast<const SMDS_MeshNode*>( anIter->next() );
10739       const SMDS_MeshNode*  aNewNode = aCurrNode;
10740       TNodeNodeMap::iterator     n2n = theNodeNodeMap.find( aCurrNode );
10741       if ( n2n != theNodeNodeMap.end() )
10742       {
10743         aNewNode = n2n->second;
10744       }
10745       else if ( theIsDoubleElem && !theNodesNot.count( aCurrNode ))
10746       {
10747         // duplicate node
10748         aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10749         copyPosition( aCurrNode, aNewNode );
10750         theNodeNodeMap[ aCurrNode ] = aNewNode;
10751         myLastCreatedNodes.push_back( aNewNode );
10752       }
10753       isDuplicate |= (aCurrNode != aNewNode);
10754       newNodes[ ind++ ] = aNewNode;
10755     }
10756     if ( !isDuplicate )
10757       continue;
10758
10759     if ( theIsDoubleElem )
10760       AddElement( newNodes, elemType.Init( anElem, /*basicOnly=*/false ));
10761     else
10762       theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], newNodes.size() );
10763
10764     res = true;
10765   }
10766   return res;
10767 }
10768
10769 //================================================================================
10770 /*!
10771   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10772   \param theNodes - identifiers of nodes to be doubled
10773   \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10774   nodes. If list of element identifiers is empty then nodes are doubled but
10775   they not assigned to elements
10776   \return TRUE if operation has been completed successfully, FALSE otherwise
10777 */
10778 //================================================================================
10779
10780 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10781                                     const std::list< int >& theListOfModifiedElems )
10782 {
10783   ClearLastCreated();
10784
10785   if ( theListOfNodes.size() == 0 )
10786     return false;
10787
10788   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10789   if ( !aMeshDS )
10790     return false;
10791
10792   // iterate through nodes and duplicate them
10793
10794   std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10795
10796   std::list< int >::const_iterator aNodeIter;
10797   for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10798   {
10799     const SMDS_MeshNode* aNode = aMeshDS->FindNode( *aNodeIter );
10800     if ( !aNode )
10801       continue;
10802
10803     // duplicate node
10804
10805     const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10806     if ( aNewNode )
10807     {
10808       copyPosition( aNode, aNewNode );
10809       anOldNodeToNewNode[ aNode ] = aNewNode;
10810       myLastCreatedNodes.push_back( aNewNode );
10811     }
10812   }
10813
10814   // Change nodes of elements
10815
10816   std::vector<const SMDS_MeshNode*> aNodeArr;
10817
10818   std::list< int >::const_iterator anElemIter;
10819   for ( anElemIter =  theListOfModifiedElems.begin();
10820         anElemIter != theListOfModifiedElems.end();
10821         anElemIter++ )
10822   {
10823     const SMDS_MeshElement* anElem = aMeshDS->FindElement( *anElemIter );
10824     if ( !anElem )
10825       continue;
10826
10827     aNodeArr.assign( anElem->begin_nodes(), anElem->end_nodes() );
10828     for( size_t i = 0; i < aNodeArr.size(); ++i )
10829     {
10830       std::map< const SMDS_MeshNode*, const SMDS_MeshNode* >::iterator n2n =
10831         anOldNodeToNewNode.find( aNodeArr[ i ]);
10832       if ( n2n != anOldNodeToNewNode.end() )
10833         aNodeArr[ i ] = n2n->second;
10834     }
10835     aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], aNodeArr.size() );
10836   }
10837
10838   return true;
10839 }
10840
10841 namespace {
10842
10843   //================================================================================
10844   /*!
10845     \brief Check if element located inside shape
10846     \return TRUE if IN or ON shape, FALSE otherwise
10847   */
10848   //================================================================================
10849
10850   template<class Classifier>
10851   bool isInside(const SMDS_MeshElement* theElem,
10852                 Classifier&             theClassifier,
10853                 const double            theTol)
10854   {
10855     gp_XYZ centerXYZ (0, 0, 0);
10856     for ( SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator(); aNodeItr->more(); )
10857       centerXYZ += SMESH_NodeXYZ( aNodeItr->next() );
10858
10859     gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10860     theClassifier.Perform(aPnt, theTol);
10861     TopAbs_State aState = theClassifier.State();
10862     return (aState == TopAbs_IN || aState == TopAbs_ON );
10863   }
10864
10865   //================================================================================
10866   /*!
10867    * \brief Classifier of the 3D point on the TopoDS_Face
10868    *        with interaface suitable for isInside()
10869    */
10870   //================================================================================
10871
10872   struct _FaceClassifier
10873   {
10874     Extrema_ExtPS       _extremum;
10875     BRepAdaptor_Surface _surface;
10876     TopAbs_State        _state;
10877
10878     _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10879     {
10880       _extremum.Initialize( _surface,
10881                             _surface.FirstUParameter(), _surface.LastUParameter(),
10882                             _surface.FirstVParameter(), _surface.LastVParameter(),
10883                             _surface.Tolerance(), _surface.Tolerance() );
10884     }
10885     void Perform(const gp_Pnt& aPnt, double theTol)
10886     {
10887       theTol *= theTol;
10888       _state = TopAbs_OUT;
10889       _extremum.Perform(aPnt);
10890       if ( _extremum.IsDone() )
10891         for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10892           _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10893     }
10894     TopAbs_State State() const
10895     {
10896       return _state;
10897     }
10898   };
10899 }
10900
10901 //================================================================================
10902 /*!
10903   \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
10904   This method is the first step of DoubleNodeElemGroupsInRegion.
10905   \param theElems - list of groups of elements (edges or faces) to be replicated
10906   \param theNodesNot - list of groups of nodes not to replicated
10907   \param theShape - shape to detect affected elements (element which geometric center
10908          located on or inside shape). If the shape is null, detection is done on faces orientations
10909          (select elements with a gravity center on the side given by faces normals).
10910          This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
10911          The replicated nodes should be associated to affected elements.
10912   \return true
10913   \sa DoubleNodeElemGroupsInRegion()
10914 */
10915 //================================================================================
10916
10917 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
10918                                                    const TIDSortedElemSet& theNodesNot,
10919                                                    const TopoDS_Shape&     theShape,
10920                                                    TIDSortedElemSet&       theAffectedElems)
10921 {
10922   if ( theShape.IsNull() )
10923   {
10924     findAffectedElems( theElems, theAffectedElems );
10925   }
10926   else
10927   {
10928     const double aTol = Precision::Confusion();
10929     std::unique_ptr< BRepClass3d_SolidClassifier> bsc3d;
10930     std::unique_ptr<_FaceClassifier>              aFaceClassifier;
10931     if ( theShape.ShapeType() == TopAbs_SOLID )
10932     {
10933       bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10934       bsc3d->PerformInfinitePoint(aTol);
10935     }
10936     else if (theShape.ShapeType() == TopAbs_FACE )
10937     {
10938       aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10939     }
10940
10941     // iterates on indicated elements and get elements by back references from their nodes
10942     TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10943     for ( ;  elemItr != theElems.end(); ++elemItr )
10944     {
10945       SMDS_MeshElement*     anElem = (SMDS_MeshElement*)*elemItr;
10946       SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10947       while ( nodeItr->more() )
10948       {
10949         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10950         if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10951           continue;
10952         SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10953         while ( backElemItr->more() )
10954         {
10955           const SMDS_MeshElement* curElem = backElemItr->next();
10956           if ( curElem && theElems.find(curElem) == theElems.end() &&
10957                ( bsc3d.get() ?
10958                  isInside( curElem, *bsc3d, aTol ) :
10959                  isInside( curElem, *aFaceClassifier, aTol )))
10960             theAffectedElems.insert( curElem );
10961         }
10962       }
10963     }
10964   }
10965   return true;
10966 }
10967
10968 //================================================================================
10969 /*!
10970   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10971   \param theElems - group of of elements (edges or faces) to be replicated
10972   \param theNodesNot - group of nodes not to replicate
10973   \param theShape - shape to detect affected elements (element which geometric center
10974   located on or inside shape).
10975   The replicated nodes should be associated to affected elements.
10976   \return TRUE if operation has been completed successfully, FALSE otherwise
10977 */
10978 //================================================================================
10979
10980 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
10981                                             const TIDSortedElemSet& theNodesNot,
10982                                             const TopoDS_Shape&     theShape )
10983 {
10984   if ( theShape.IsNull() )
10985     return false;
10986
10987   const double aTol = Precision::Confusion();
10988   SMESHUtils::Deleter< BRepClass3d_SolidClassifier> bsc3d;
10989   SMESHUtils::Deleter<_FaceClassifier>              aFaceClassifier;
10990   if ( theShape.ShapeType() == TopAbs_SOLID )
10991   {
10992     bsc3d._obj = new BRepClass3d_SolidClassifier( theShape );
10993     bsc3d->PerformInfinitePoint(aTol);
10994   }
10995   else if (theShape.ShapeType() == TopAbs_FACE )
10996   {
10997     aFaceClassifier._obj = new _FaceClassifier( TopoDS::Face( theShape ));
10998   }
10999
11000   // iterates on indicated elements and get elements by back references from their nodes
11001   TIDSortedElemSet anAffected;
11002   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11003   for ( ;  elemItr != theElems.end(); ++elemItr )
11004   {
11005     SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11006     if (!anElem)
11007       continue;
11008
11009     SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11010     while ( nodeItr->more() )
11011     {
11012       const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11013       if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11014         continue;
11015       SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11016       while ( backElemItr->more() )
11017       {
11018         const SMDS_MeshElement* curElem = backElemItr->next();
11019         if ( curElem && theElems.find(curElem) == theElems.end() &&
11020              ( bsc3d ?
11021                isInside( curElem, *bsc3d, aTol ) :
11022                isInside( curElem, *aFaceClassifier, aTol )))
11023           anAffected.insert( curElem );
11024       }
11025     }
11026   }
11027   return DoubleNodes( theElems, theNodesNot, anAffected );
11028 }
11029
11030 /*!
11031  *  \brief compute an oriented angle between two planes defined by four points.
11032  *  The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
11033  *  @param p0 base of the rotation axe
11034  *  @param p1 extremity of the rotation axe
11035  *  @param g1 belongs to the first plane
11036  *  @param g2 belongs to the second plane
11037  */
11038 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
11039 {
11040   gp_Vec vref(p0, p1);
11041   gp_Vec v1(p0, g1);
11042   gp_Vec v2(p0, g2);
11043   gp_Vec n1 = vref.Crossed(v1);
11044   gp_Vec n2 = vref.Crossed(v2);
11045   try {
11046     return n2.AngleWithRef(n1, vref);
11047   }
11048   catch ( Standard_Failure ) {
11049   }
11050   return Max( v1.Magnitude(), v2.Magnitude() );
11051 }
11052
11053 /*!
11054  * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11055  *  The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
11056  * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
11057  * Triangles are transformed into prisms, and quadrangles into hexahedrons.
11058  * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
11059  * 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.
11060  * 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.
11061  * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
11062  * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
11063  * \param theElems - list of groups of volumes, where a group of volume is a set of
11064  *        SMDS_MeshElements sorted by Id.
11065  * \param createJointElems - if TRUE, create the elements
11066  * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
11067  *        the boundary between \a theDomains and the rest mesh
11068  * \return TRUE if operation has been completed successfully, FALSE otherwise
11069  */
11070 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11071                                                      bool                                 createJointElems,
11072                                                      bool                                 onAllBoundaries)
11073 {
11074   // MESSAGE("----------------------------------------------");
11075   // MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11076   // MESSAGE("----------------------------------------------");
11077
11078   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11079   meshDS->BuildDownWardConnectivity(true);
11080   CHRONO(50);
11081   SMDS_UnstructuredGrid *grid = meshDS->GetGrid();
11082
11083   // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11084   //     build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11085   //     build the list of nodes shared by 2 or more domains, with their domain indexes
11086
11087   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11088   std::map<int,int>celldom; // cell vtkId --> domain
11089   std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains;  // oldNode --> (id domain --> id cell)
11090   std::map<int, std::map<int,int> > nodeDomains; // oldId -->  (domainId --> newId)
11091   faceDomains.clear();
11092   celldom.clear();
11093   cellDomains.clear();
11094   nodeDomains.clear();
11095   std::map<int,int> emptyMap;
11096   std::set<int> emptySet;
11097   emptyMap.clear();
11098
11099   //MESSAGE(".. Number of domains :"<<theElems.size());
11100
11101   TIDSortedElemSet theRestDomElems;
11102   const int iRestDom  = -1;
11103   const int idom0     = onAllBoundaries ? iRestDom : 0;
11104   const int nbDomains = theElems.size();
11105
11106   // Check if the domains do not share an element
11107   for (int idom = 0; idom < nbDomains-1; idom++)
11108   {
11109     //       MESSAGE("... Check of domain #" << idom);
11110     const TIDSortedElemSet& domain = theElems[idom];
11111     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11112     for (; elemItr != domain.end(); ++elemItr)
11113     {
11114       const SMDS_MeshElement* anElem = *elemItr;
11115       int idombisdeb = idom + 1 ;
11116       // check if the element belongs to a domain further in the list
11117       for ( size_t idombis = idombisdeb; idombis < theElems.size(); idombis++ )
11118       {
11119         const TIDSortedElemSet& domainbis = theElems[idombis];
11120         if ( domainbis.count( anElem ))
11121         {
11122           MESSAGE(".... Domain #" << idom);
11123           MESSAGE(".... Domain #" << idombis);
11124           throw SALOME_Exception("The domains are not disjoint.");
11125           return false ;
11126         }
11127       }
11128     }
11129   }
11130
11131   for (int idom = 0; idom < nbDomains; idom++)
11132   {
11133
11134     // --- build a map (face to duplicate --> volume to modify)
11135     //     with all the faces shared by 2 domains (group of elements)
11136     //     and corresponding volume of this domain, for each shared face.
11137     //     a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11138
11139     //MESSAGE("... Neighbors of domain #" << idom);
11140     const TIDSortedElemSet& domain = theElems[idom];
11141     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11142     for (; elemItr != domain.end(); ++elemItr)
11143     {
11144       const SMDS_MeshElement* anElem = *elemItr;
11145       if (!anElem)
11146         continue;
11147       int vtkId = anElem->GetVtkID();
11148       //MESSAGE("  vtkId " << vtkId << " smdsId " << anElem->GetID());
11149       int neighborsVtkIds[NBMAXNEIGHBORS];
11150       int downIds[NBMAXNEIGHBORS];
11151       unsigned char downTypes[NBMAXNEIGHBORS];
11152       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11153       for (int n = 0; n < nbNeighbors; n++)
11154       {
11155         int smdsId = meshDS->FromVtkToSmds(neighborsVtkIds[n]);
11156         const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11157         if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
11158         {
11159           bool ok = false;
11160           for ( size_t idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
11161           {
11162             // MESSAGE("Domain " << idombis);
11163             const TIDSortedElemSet& domainbis = theElems[idombis];
11164             if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11165           }
11166           if ( ok || onAllBoundaries ) // the characteristics of the face is stored
11167           {
11168             DownIdType face(downIds[n], downTypes[n]);
11169             if (!faceDomains[face].count(idom))
11170             {
11171               faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11172               celldom[vtkId] = idom;
11173               //MESSAGE("       cell with a border " << vtkId << " domain " << idom);
11174             }
11175             if ( !ok )
11176             {
11177               theRestDomElems.insert( elem );
11178               faceDomains[face][iRestDom] = neighborsVtkIds[n];
11179               celldom[neighborsVtkIds[n]] = iRestDom;
11180             }
11181           }
11182         }
11183       }
11184     }
11185   }
11186
11187   //MESSAGE("Number of shared faces " << faceDomains.size());
11188   std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11189
11190   // --- explore the shared faces domain by domain,
11191   //     explore the nodes of the face and see if they belong to a cell in the domain,
11192   //     which has only a node or an edge on the border (not a shared face)
11193
11194   for (int idomain = idom0; idomain < nbDomains; idomain++)
11195   {
11196     //MESSAGE("Domain " << idomain);
11197     const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
11198     itface = faceDomains.begin();
11199     for (; itface != faceDomains.end(); ++itface)
11200     {
11201       const std::map<int, int>& domvol = itface->second;
11202       if (!domvol.count(idomain))
11203         continue;
11204       DownIdType face = itface->first;
11205       //MESSAGE(" --- face " << face.cellId);
11206       std::set<int> oldNodes;
11207       oldNodes.clear();
11208       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11209       std::set<int>::iterator itn = oldNodes.begin();
11210       for (; itn != oldNodes.end(); ++itn)
11211       {
11212         int oldId = *itn;
11213         //MESSAGE("     node " << oldId);
11214         vtkCellLinks::Link l = (static_cast <vtkCellLinks *>(grid->GetCellLinks()))->GetLink(oldId);
11215         for (int i=0; i<l.ncells; i++)
11216         {
11217           int vtkId = l.cells[i];
11218           const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->FromVtkToSmds(vtkId));
11219           if (!domain.count(anElem))
11220             continue;
11221           int vtkType = grid->GetCellType(vtkId);
11222           int downId = grid->CellIdToDownId(vtkId);
11223           if (downId < 0)
11224           {
11225             MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11226             continue; // not OK at this stage of the algorithm:
11227             //no cells created after BuildDownWardConnectivity
11228           }
11229           DownIdType aCell(downId, vtkType);
11230           cellDomains[aCell][idomain] = vtkId;
11231           celldom[vtkId] = idomain;
11232           //MESSAGE("       cell " << vtkId << " domain " << idomain);
11233         }
11234       }
11235     }
11236   }
11237
11238   // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11239   //     for each shared face, get the nodes
11240   //     for each node, for each domain of the face, create a clone of the node
11241
11242   // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11243   //     junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11244   //     the value is the ordered domain ids. (more than 4 domains not taken into account)
11245
11246   std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11247   std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11248   std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11249
11250   //MESSAGE(".. Duplication of the nodes");
11251   for (int idomain = idom0; idomain < nbDomains; idomain++)
11252   {
11253     itface = faceDomains.begin();
11254     for (; itface != faceDomains.end(); ++itface)
11255     {
11256       const std::map<int, int>& domvol = itface->second;
11257       if (!domvol.count(idomain))
11258         continue;
11259       DownIdType face = itface->first;
11260       //MESSAGE(" --- face " << face.cellId);
11261       std::set<int> oldNodes;
11262       oldNodes.clear();
11263       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11264       std::set<int>::iterator itn = oldNodes.begin();
11265       for (; itn != oldNodes.end(); ++itn)
11266       {
11267         int oldId = *itn;
11268         if (nodeDomains[oldId].empty())
11269         {
11270           nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11271           //MESSAGE("-+-+-b     oldNode " << oldId << " domain " << idomain);
11272         }
11273         std::map<int, int>::const_iterator itdom = domvol.begin();
11274         for (; itdom != domvol.end(); ++itdom)
11275         {
11276           int idom = itdom->first;
11277           //MESSAGE("         domain " << idom);
11278           if (!nodeDomains[oldId].count(idom)) // --- node to clone
11279           {
11280             if (nodeDomains[oldId].size() >= 2) // a multiple node
11281             {
11282               vector<int> orderedDoms;
11283               //MESSAGE("multiple node " << oldId);
11284               if (mutipleNodes.count(oldId))
11285                 orderedDoms = mutipleNodes[oldId];
11286               else
11287               {
11288                 map<int,int>::iterator it = nodeDomains[oldId].begin();
11289                 for (; it != nodeDomains[oldId].end(); ++it)
11290                   orderedDoms.push_back(it->first);
11291               }
11292               orderedDoms.push_back(idom); // TODO order ==> push_front or back
11293               //stringstream txt;
11294               //for (int i=0; i<orderedDoms.size(); i++)
11295               //  txt << orderedDoms[i] << " ";
11296               //MESSAGE("orderedDoms " << txt.str());
11297               mutipleNodes[oldId] = orderedDoms;
11298             }
11299             double *coords = grid->GetPoint(oldId);
11300             SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11301             copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11302             int newId = newNode->GetVtkID();
11303             nodeDomains[oldId][idom] = newId; // cloned node for other domains
11304             //MESSAGE("-+-+-c     oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11305           }
11306         }
11307       }
11308     }
11309   }
11310
11311   //MESSAGE(".. Creation of elements");
11312   for (int idomain = idom0; idomain < nbDomains; idomain++)
11313   {
11314     itface = faceDomains.begin();
11315     for (; itface != faceDomains.end(); ++itface)
11316     {
11317       std::map<int, int> domvol = itface->second;
11318       if (!domvol.count(idomain))
11319         continue;
11320       DownIdType face = itface->first;
11321       //MESSAGE(" --- face " << face.cellId);
11322       std::set<int> oldNodes;
11323       oldNodes.clear();
11324       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11325       int nbMultipleNodes = 0;
11326       std::set<int>::iterator itn = oldNodes.begin();
11327       for (; itn != oldNodes.end(); ++itn)
11328       {
11329         int oldId = *itn;
11330         if (mutipleNodes.count(oldId))
11331           nbMultipleNodes++;
11332       }
11333       if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11334       {
11335         //MESSAGE("multiple Nodes detected on a shared face");
11336         int downId = itface->first.cellId;
11337         unsigned char cellType = itface->first.cellType;
11338         // --- shared edge or shared face ?
11339         if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11340         {
11341           int nodes[3];
11342           int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11343           for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11344             if (mutipleNodes.count(nodes[i]))
11345               if (!mutipleNodesToFace.count(nodes[i]))
11346                 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11347         }
11348         else // shared face (between two volumes)
11349         {
11350           int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11351           const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11352           const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11353           for (int ie =0; ie < nbEdges; ie++)
11354           {
11355             int nodes[3];
11356             int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11357             if ( mutipleNodes.count(nodes[0]) && mutipleNodes.count( nodes[ nbNodes-1 ]))
11358             {
11359               vector<int> vn0 = mutipleNodes[nodes[0]];
11360               vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11361               vector<int> doms;
11362               for ( size_t i0 = 0; i0 < vn0.size(); i0++ )
11363                 for ( size_t i1 = 0; i1 < vn1.size(); i1++ )
11364                   if ( vn0[i0] == vn1[i1] )
11365                     doms.push_back( vn0[ i0 ]);
11366               if ( doms.size() > 2 )
11367               {
11368                 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11369                 double *coords = grid->GetPoint(nodes[0]);
11370                 gp_Pnt p0(coords[0], coords[1], coords[2]);
11371                 coords = grid->GetPoint(nodes[nbNodes - 1]);
11372                 gp_Pnt p1(coords[0], coords[1], coords[2]);
11373                 gp_Pnt gref;
11374                 int vtkVolIds[1000];  // an edge can belong to a lot of volumes
11375                 map<int, SMDS_MeshVolume*> domvol; // domain --> a volume with the edge
11376                 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11377                 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11378                 for ( size_t id = 0; id < doms.size(); id++ )
11379                 {
11380                   int idom = doms[id];
11381                   const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11382                   for ( int ivol = 0; ivol < nbvol; ivol++ )
11383                   {
11384                     int smdsId = meshDS->FromVtkToSmds(vtkVolIds[ivol]);
11385                     const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11386                     if (domain.count(elem))
11387                     {
11388                       const SMDS_MeshVolume* svol = SMDS_Mesh::DownCast<SMDS_MeshVolume>(elem);
11389                       domvol[idom] = (SMDS_MeshVolume*) svol;
11390                       //MESSAGE("  domain " << idom << " volume " << elem->GetID());
11391                       double values[3] = { 0,0,0 };
11392                       vtkIdType npts = 0;
11393                       vtkIdType const *pts(nullptr);
11394                       grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11395                       for ( vtkIdType i = 0; i < npts; ++i )
11396                       {
11397                         double *coords = grid->GetPoint( pts[i] );
11398                         for ( int j = 0; j < 3; ++j )
11399                           values[j] += coords[j] / npts;
11400                       }
11401                       if ( id == 0 )
11402                       {
11403                         gref.SetCoord( values[0], values[1], values[2] );
11404                         angleDom[idom] = 0;
11405                       }
11406                       else
11407                       {
11408                         gp_Pnt g( values[0], values[1], values[2] );
11409                         angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11410                         //MESSAGE("  angle=" << angleDom[idom]);
11411                       }
11412                       break;
11413                     }
11414                   }
11415                 }
11416                 map<double, int> sortedDom; // sort domains by angle
11417                 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11418                   sortedDom[ia->second] = ia->first;
11419                 vector<int> vnodes;
11420                 vector<int> vdom;
11421                 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11422                 {
11423                   vdom.push_back(ib->second);
11424                   //MESSAGE("  ordered domain " << ib->second << "  angle " << ib->first);
11425                 }
11426                 for (int ino = 0; ino < nbNodes; ino++)
11427                   vnodes.push_back(nodes[ino]);
11428                 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11429               }
11430             }
11431           }
11432         }
11433       }
11434     }
11435   }
11436
11437   // --- iterate on shared faces (volumes to modify, face to extrude)
11438   //     get node id's of the face (id SMDS = id VTK)
11439   //     create flat element with old and new nodes if requested
11440
11441   // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11442   //     (domain1 X domain2) = domain1 + MAXINT*domain2
11443
11444   std::map<int, std::map<long,int> > nodeQuadDomains;
11445   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11446
11447   //MESSAGE(".. Creation of elements: simple junction");
11448   if (createJointElems)
11449   {
11450     string joints2DName = "joints2D";
11451     mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str());
11452     SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11453     string joints3DName = "joints3D";
11454     mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str());
11455     SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11456
11457     itface = faceDomains.begin();
11458     for (; itface != faceDomains.end(); ++itface)
11459     {
11460       DownIdType face = itface->first;
11461       std::set<int> oldNodes;
11462       std::set<int>::iterator itn;
11463       oldNodes.clear();
11464       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11465
11466       std::map<int, int> domvol = itface->second;
11467       std::map<int, int>::iterator itdom = domvol.begin();
11468       int dom1 = itdom->first;
11469       int vtkVolId = itdom->second;
11470       itdom++;
11471       int dom2 = itdom->first;
11472       SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11473                                                        nodeQuadDomains);
11474       stringstream grpname;
11475       grpname << "j_";
11476       if (dom1 < dom2)
11477         grpname << dom1 << "_" << dom2;
11478       else
11479         grpname << dom2 << "_" << dom1;
11480       string namegrp = grpname.str();
11481       if (!mapOfJunctionGroups.count(namegrp))
11482         mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str());
11483       SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11484       if (sgrp)
11485         sgrp->Add(vol->GetID());
11486       if (vol->GetType() == SMDSAbs_Volume)
11487         joints3DGrp->Add(vol->GetID());
11488       else if (vol->GetType() == SMDSAbs_Face)
11489         joints2DGrp->Add(vol->GetID());
11490     }
11491   }
11492
11493   // --- create volumes on multiple domain intersection if requested
11494   //     iterate on mutipleNodesToFace
11495   //     iterate on edgesMultiDomains
11496
11497   //MESSAGE(".. Creation of elements: multiple junction");
11498   if (createJointElems)
11499   {
11500     // --- iterate on mutipleNodesToFace
11501
11502     std::map<int, std::vector<int> >::iterator itn =  mutipleNodesToFace.begin();
11503     for (; itn != mutipleNodesToFace.end(); ++itn)
11504     {
11505       int node = itn->first;
11506       vector<int> orderDom = itn->second;
11507       vector<vtkIdType> orderedNodes;
11508       for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11509         orderedNodes.push_back( nodeDomains[ node ][ orderDom[ idom ]]);
11510       SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11511
11512       stringstream grpname;
11513       grpname << "m2j_";
11514       grpname << 0 << "_" << 0;
11515       string namegrp = grpname.str();
11516       if (!mapOfJunctionGroups.count(namegrp))
11517         mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str());
11518       SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11519       if (sgrp)
11520         sgrp->Add(face->GetID());
11521     }
11522
11523     // --- iterate on edgesMultiDomains
11524
11525     std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11526     for (; ite != edgesMultiDomains.end(); ++ite)
11527     {
11528       vector<int> nodes = ite->first;
11529       vector<int> orderDom = ite->second;
11530       vector<vtkIdType> orderedNodes;
11531       if (nodes.size() == 2)
11532       {
11533         //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11534         for ( size_t ino = 0; ino < nodes.size(); ino++ )
11535           if ( orderDom.size() == 3 )
11536             for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11537               orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11538           else
11539             for (int idom = orderDom.size()-1; idom >=0; idom--)
11540               orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11541         SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11542
11543         string namegrp = "jointsMultiples";
11544         if (!mapOfJunctionGroups.count(namegrp))
11545           mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str());
11546         SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11547         if (sgrp)
11548           sgrp->Add(vol->GetID());
11549       }
11550       else
11551       {
11552         //INFOS("Quadratic multiple joints not implemented");
11553         // TODO quadratic nodes
11554       }
11555     }
11556   }
11557
11558   // --- list the explicit faces and edges of the mesh that need to be modified,
11559   //     i.e. faces and edges built with one or more duplicated nodes.
11560   //     associate these faces or edges to their corresponding domain.
11561   //     only the first domain found is kept when a face or edge is shared
11562
11563   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11564   std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11565   faceOrEdgeDom.clear();
11566   feDom.clear();
11567
11568   //MESSAGE(".. Modification of elements");
11569   for (int idomain = idom0; idomain < nbDomains; idomain++)
11570   {
11571     std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11572     for (; itnod != nodeDomains.end(); ++itnod)
11573     {
11574       int oldId = itnod->first;
11575       //MESSAGE("     node " << oldId);
11576       vtkCellLinks::Link l = (static_cast< vtkCellLinks *>(grid->GetCellLinks()))->GetLink(oldId);
11577       for (int i = 0; i < l.ncells; i++)
11578       {
11579         int vtkId = l.cells[i];
11580         int vtkType = grid->GetCellType(vtkId);
11581         int downId = grid->CellIdToDownId(vtkId);
11582         if (downId < 0)
11583           continue; // new cells: not to be modified
11584         DownIdType aCell(downId, vtkType);
11585         int volParents[1000];
11586         int nbvol = grid->GetParentVolumes(volParents, vtkId);
11587         for (int j = 0; j < nbvol; j++)
11588           if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11589             if (!feDom.count(vtkId))
11590             {
11591               feDom[vtkId] = idomain;
11592               faceOrEdgeDom[aCell] = emptyMap;
11593               faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11594               //MESSAGE("affect cell " << this->GetMeshDS()->FromVtkToSmds(vtkId) << " domain " << idomain
11595               //        << " type " << vtkType << " downId " << downId);
11596             }
11597       }
11598     }
11599   }
11600
11601   // --- iterate on shared faces (volumes to modify, face to extrude)
11602   //     get node id's of the face
11603   //     replace old nodes by new nodes in volumes, and update inverse connectivity
11604
11605   std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11606   for (int m=0; m<3; m++)
11607   {
11608     std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11609     itface = (*amap).begin();
11610     for (; itface != (*amap).end(); ++itface)
11611     {
11612       DownIdType face = itface->first;
11613       std::set<int> oldNodes;
11614       std::set<int>::iterator itn;
11615       oldNodes.clear();
11616       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11617       //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11618       std::map<int, int> localClonedNodeIds;
11619
11620       std::map<int, int> domvol = itface->second;
11621       std::map<int, int>::iterator itdom = domvol.begin();
11622       for (; itdom != domvol.end(); ++itdom)
11623       {
11624         int idom = itdom->first;
11625         int vtkVolId = itdom->second;
11626         //MESSAGE("modify nodes of cell " << this->GetMeshDS()->FromVtkToSmds(vtkVolId) << " domain " << idom);
11627         localClonedNodeIds.clear();
11628         for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11629         {
11630           int oldId = *itn;
11631           if (nodeDomains[oldId].count(idom))
11632           {
11633             localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11634             //MESSAGE("     node " << oldId << " --> " << localClonedNodeIds[oldId]);
11635           }
11636         }
11637         meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11638       }
11639     }
11640   }
11641
11642   // Remove empty groups (issue 0022812)
11643   std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
11644   for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
11645   {
11646     if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
11647       myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
11648   }
11649
11650   meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11651   grid->DeleteLinks();
11652
11653   CHRONOSTOP(50);
11654   counters::stats();
11655   return true;
11656 }
11657
11658 /*!
11659  * \brief Double nodes on some external faces and create flat elements.
11660  * Flat elements are mainly used by some types of mechanic calculations.
11661  *
11662  * Each group of the list must be constituted of faces.
11663  * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11664  * @param theElems - list of groups of faces, where a group of faces is a set of
11665  * SMDS_MeshElements sorted by Id.
11666  * @return TRUE if operation has been completed successfully, FALSE otherwise
11667  */
11668 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11669 {
11670   // MESSAGE("-------------------------------------------------");
11671   // MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11672   // MESSAGE("-------------------------------------------------");
11673
11674   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11675
11676   // --- For each group of faces
11677   //     duplicate the nodes, create a flat element based on the face
11678   //     replace the nodes of the faces by their clones
11679
11680   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11681   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11682   clonedNodes.clear();
11683   intermediateNodes.clear();
11684   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11685   mapOfJunctionGroups.clear();
11686
11687   for ( size_t idom = 0; idom < theElems.size(); idom++ )
11688   {
11689     const TIDSortedElemSet&           domain = theElems[idom];
11690     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11691     for ( ; elemItr != domain.end(); ++elemItr )
11692     {
11693       const SMDS_MeshFace* aFace = meshDS->DownCast<SMDS_MeshFace> ( *elemItr );
11694       if (!aFace)
11695         continue;
11696       // MESSAGE("aFace=" << aFace->GetID());
11697       bool isQuad = aFace->IsQuadratic();
11698       vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11699
11700       // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11701
11702       SMDS_NodeIteratorPtr nodeIt = aFace->nodeIterator();
11703       while (nodeIt->more())
11704       {
11705         const SMDS_MeshNode* node = nodeIt->next();
11706         bool isMedium = ( isQuad && aFace->IsMediumNode( node ));
11707         if (isMedium)
11708           ln2.push_back(node);
11709         else
11710           ln0.push_back(node);
11711
11712         const SMDS_MeshNode* clone = 0;
11713         if (!clonedNodes.count(node))
11714         {
11715           clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11716           copyPosition( node, clone );
11717           clonedNodes[node] = clone;
11718         }
11719         else
11720           clone = clonedNodes[node];
11721
11722         if (isMedium)
11723           ln3.push_back(clone);
11724         else
11725           ln1.push_back(clone);
11726
11727         const SMDS_MeshNode* inter = 0;
11728         if (isQuad && (!isMedium))
11729         {
11730           if (!intermediateNodes.count(node))
11731           {
11732             inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11733             copyPosition( node, inter );
11734             intermediateNodes[node] = inter;
11735           }
11736           else
11737             inter = intermediateNodes[node];
11738           ln4.push_back(inter);
11739         }
11740       }
11741
11742       // --- extrude the face
11743
11744       vector<const SMDS_MeshNode*> ln;
11745       SMDS_MeshVolume* vol = 0;
11746       vtkIdType aType = aFace->GetVtkType();
11747       switch (aType)
11748       {
11749       case VTK_TRIANGLE:
11750         vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11751         // MESSAGE("vol prism " << vol->GetID());
11752         ln.push_back(ln1[0]);
11753         ln.push_back(ln1[1]);
11754         ln.push_back(ln1[2]);
11755         break;
11756       case VTK_QUAD:
11757         vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11758         // MESSAGE("vol hexa " << vol->GetID());
11759         ln.push_back(ln1[0]);
11760         ln.push_back(ln1[1]);
11761         ln.push_back(ln1[2]);
11762         ln.push_back(ln1[3]);
11763         break;
11764       case VTK_QUADRATIC_TRIANGLE:
11765         vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11766                                 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11767         // MESSAGE("vol quad prism " << vol->GetID());
11768         ln.push_back(ln1[0]);
11769         ln.push_back(ln1[1]);
11770         ln.push_back(ln1[2]);
11771         ln.push_back(ln3[0]);
11772         ln.push_back(ln3[1]);
11773         ln.push_back(ln3[2]);
11774         break;
11775       case VTK_QUADRATIC_QUAD:
11776         //              vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11777         //                                      ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11778         //                                      ln4[0], ln4[1], ln4[2], ln4[3]);
11779         vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11780                                 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11781                                 ln4[0], ln4[1], ln4[2], ln4[3]);
11782         // MESSAGE("vol quad hexa " << vol->GetID());
11783         ln.push_back(ln1[0]);
11784         ln.push_back(ln1[1]);
11785         ln.push_back(ln1[2]);
11786         ln.push_back(ln1[3]);
11787         ln.push_back(ln3[0]);
11788         ln.push_back(ln3[1]);
11789         ln.push_back(ln3[2]);
11790         ln.push_back(ln3[3]);
11791         break;
11792       case VTK_POLYGON:
11793         break;
11794       default:
11795         break;
11796       }
11797
11798       if (vol)
11799       {
11800         stringstream grpname;
11801         grpname << "jf_";
11802         grpname << idom;
11803         string namegrp = grpname.str();
11804         if (!mapOfJunctionGroups.count(namegrp))
11805           mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str());
11806         SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11807         if (sgrp)
11808           sgrp->Add(vol->GetID());
11809       }
11810
11811       // --- modify the face
11812
11813       const_cast<SMDS_MeshFace*>( aFace )->ChangeNodes( &ln[0], ln.size() );
11814     }
11815   }
11816   return true;
11817 }
11818
11819 /*!
11820  *  \brief identify all the elements around a geom shape, get the faces delimiting the hole
11821  *  Build groups of volume to remove, groups of faces to replace on the skin of the object,
11822  *  groups of faces to remove inside the object, (idem edges).
11823  *  Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11824  */
11825 void SMESH_MeshEditor::CreateHoleSkin(double                          radius,
11826                                       const TopoDS_Shape&             theShape,
11827                                       SMESH_NodeSearcher*             theNodeSearcher,
11828                                       const char*                     groupName,
11829                                       std::vector<double>&            nodesCoords,
11830                                       std::vector<std::vector<int> >& listOfListOfNodes)
11831 {
11832   // MESSAGE("--------------------------------");
11833   // MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11834   // MESSAGE("--------------------------------");
11835
11836   // --- zone of volumes to remove is given :
11837   //     1 either by a geom shape (one or more vertices) and a radius,
11838   //     2 either by a group of nodes (representative of the shape)to use with the radius,
11839   //     3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11840   //     In the case 2, the group of nodes is an external group of nodes from another mesh,
11841   //     In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11842   //     defined by it's name.
11843
11844   SMESHDS_GroupBase* groupDS = 0;
11845   SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11846   while ( groupIt->more() )
11847   {
11848     groupDS = 0;
11849     SMESH_Group * group = groupIt->next();
11850     if ( !group ) continue;
11851     groupDS = group->GetGroupDS();
11852     if ( !groupDS || groupDS->IsEmpty() ) continue;
11853     std::string grpName = group->GetName();
11854     //MESSAGE("grpName=" << grpName);
11855     if (grpName == groupName)
11856       break;
11857     else
11858       groupDS = 0;
11859   }
11860
11861   bool isNodeGroup = false;
11862   bool isNodeCoords = false;
11863   if (groupDS)
11864   {
11865     if (groupDS->GetType() != SMDSAbs_Node)
11866       return;
11867     isNodeGroup = true;     // a group of nodes exists and it is in this mesh
11868   }
11869
11870   if (nodesCoords.size() > 0)
11871     isNodeCoords = true; // a list o nodes given by their coordinates
11872   //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11873
11874   // --- define groups to build
11875
11876   // --- group of SMDS volumes
11877   string grpvName = groupName;
11878   grpvName += "_vol";
11879   SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str());
11880   if (!grp)
11881   {
11882     MESSAGE("group not created " << grpvName);
11883     return;
11884   }
11885   SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
11886
11887   // --- group of SMDS faces on the skin
11888   string grpsName = groupName;
11889   grpsName += "_skin";
11890   SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str());
11891   if (!grps)
11892   {
11893     MESSAGE("group not created " << grpsName);
11894     return;
11895   }
11896   SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
11897
11898   // --- group of SMDS faces internal (several shapes)
11899   string grpiName = groupName;
11900   grpiName += "_internalFaces";
11901   SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str());
11902   if (!grpi)
11903   {
11904     MESSAGE("group not created " << grpiName);
11905     return;
11906   }
11907   SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
11908
11909   // --- group of SMDS faces internal (several shapes)
11910   string grpeiName = groupName;
11911   grpeiName += "_internalEdges";
11912   SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str());
11913   if (!grpei)
11914   {
11915     MESSAGE("group not created " << grpeiName);
11916     return;
11917   }
11918   SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
11919
11920   // --- build downward connectivity
11921
11922   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11923   meshDS->BuildDownWardConnectivity(true);
11924   SMDS_UnstructuredGrid* grid = meshDS->GetGrid();
11925
11926   // --- set of volumes detected inside
11927
11928   std::set<int> setOfInsideVol;
11929   std::set<int> setOfVolToCheck;
11930
11931   std::vector<gp_Pnt> gpnts;
11932   gpnts.clear();
11933
11934   if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
11935   {
11936     //MESSAGE("group of nodes provided");
11937     SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
11938     while ( elemIt->more() )
11939     {
11940       const SMDS_MeshElement* elem = elemIt->next();
11941       if (!elem)
11942         continue;
11943       const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
11944       if (!node)
11945         continue;
11946       SMDS_MeshElement* vol = 0;
11947       SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
11948       while (volItr->more())
11949       {
11950         vol = (SMDS_MeshElement*)volItr->next();
11951         setOfInsideVol.insert(vol->GetVtkID());
11952         sgrp->Add(vol->GetID());
11953       }
11954     }
11955   }
11956   else if (isNodeCoords)
11957   {
11958     //MESSAGE("list of nodes coordinates provided");
11959     size_t i = 0;
11960     int k = 0;
11961     while ( i < nodesCoords.size()-2 )
11962     {
11963       double x = nodesCoords[i++];
11964       double y = nodesCoords[i++];
11965       double z = nodesCoords[i++];
11966       gp_Pnt p = gp_Pnt(x, y ,z);
11967       gpnts.push_back(p);
11968       //MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
11969       k++;
11970     }
11971   }
11972   else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
11973   {
11974     //MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
11975     TopTools_IndexedMapOfShape vertexMap;
11976     TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
11977     gp_Pnt p = gp_Pnt(0,0,0);
11978     if (vertexMap.Extent() < 1)
11979       return;
11980
11981     for ( int i = 1; i <= vertexMap.Extent(); ++i )
11982     {
11983       const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
11984       p = BRep_Tool::Pnt(vertex);
11985       gpnts.push_back(p);
11986       //MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
11987     }
11988   }
11989
11990   if (gpnts.size() > 0)
11991   {
11992     const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
11993     //MESSAGE("startNode->nodeId " << nodeId);
11994
11995     double radius2 = radius*radius;
11996     //MESSAGE("radius2 " << radius2);
11997
11998     // --- volumes on start node
11999
12000     setOfVolToCheck.clear();
12001     SMDS_MeshElement* startVol = 0;
12002     SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
12003     while (volItr->more())
12004     {
12005       startVol = (SMDS_MeshElement*)volItr->next();
12006       setOfVolToCheck.insert(startVol->GetVtkID());
12007     }
12008     if (setOfVolToCheck.empty())
12009     {
12010       MESSAGE("No volumes found");
12011       return;
12012     }
12013
12014     // --- starting with central volumes then their neighbors, check if they are inside
12015     //     or outside the domain, until no more new neighbor volume is inside.
12016     //     Fill the group of inside volumes
12017
12018     std::map<int, double> mapOfNodeDistance2;
12019     mapOfNodeDistance2.clear();
12020     std::set<int> setOfOutsideVol;
12021     while (!setOfVolToCheck.empty())
12022     {
12023       std::set<int>::iterator it = setOfVolToCheck.begin();
12024       int vtkId = *it;
12025       //MESSAGE("volume to check,  vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12026       bool volInside = false;
12027       vtkIdType npts = 0;
12028       vtkIdType const *pts(nullptr);
12029       grid->GetCellPoints(vtkId, npts, pts);
12030       for (int i=0; i<npts; i++)
12031       {
12032         double distance2 = 0;
12033         if (mapOfNodeDistance2.count(pts[i]))
12034         {
12035           distance2 = mapOfNodeDistance2[pts[i]];
12036           //MESSAGE("point " << pts[i] << " distance2 " << distance2);
12037         }
12038         else
12039         {
12040           double *coords = grid->GetPoint(pts[i]);
12041           gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
12042           distance2 = 1.E40;
12043           for ( size_t j = 0; j < gpnts.size(); j++ )
12044           {
12045             double d2 = aPoint.SquareDistance( gpnts[ j ]);
12046             if (d2 < distance2)
12047             {
12048               distance2 = d2;
12049               if (distance2 < radius2)
12050                 break;
12051             }
12052           }
12053           mapOfNodeDistance2[pts[i]] = distance2;
12054           //MESSAGE("  point "  << pts[i]  << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " <<  coords[2]);
12055         }
12056         if (distance2 < radius2)
12057         {
12058           volInside = true; // one or more nodes inside the domain
12059           sgrp->Add(meshDS->FromVtkToSmds(vtkId));
12060           break;
12061         }
12062       }
12063       if (volInside)
12064       {
12065         setOfInsideVol.insert(vtkId);
12066         //MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12067         int neighborsVtkIds[NBMAXNEIGHBORS];
12068         int downIds[NBMAXNEIGHBORS];
12069         unsigned char downTypes[NBMAXNEIGHBORS];
12070         int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12071         for (int n = 0; n < nbNeighbors; n++)
12072           if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12073             setOfVolToCheck.insert(neighborsVtkIds[n]);
12074       }
12075       else
12076       {
12077         setOfOutsideVol.insert(vtkId);
12078         //MESSAGE("  volume outside, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12079       }
12080       setOfVolToCheck.erase(vtkId);
12081     }
12082   }
12083
12084   // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12085   //     If yes, add the volume to the inside set
12086
12087   bool addedInside = true;
12088   std::set<int> setOfVolToReCheck;
12089   while (addedInside)
12090   {
12091     //MESSAGE(" --------------------------- re check");
12092     addedInside = false;
12093     std::set<int>::iterator itv = setOfInsideVol.begin();
12094     for (; itv != setOfInsideVol.end(); ++itv)
12095     {
12096       int vtkId = *itv;
12097       int neighborsVtkIds[NBMAXNEIGHBORS];
12098       int downIds[NBMAXNEIGHBORS];
12099       unsigned char downTypes[NBMAXNEIGHBORS];
12100       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12101       for (int n = 0; n < nbNeighbors; n++)
12102         if (!setOfInsideVol.count(neighborsVtkIds[n]))
12103           setOfVolToReCheck.insert(neighborsVtkIds[n]);
12104     }
12105     setOfVolToCheck = setOfVolToReCheck;
12106     setOfVolToReCheck.clear();
12107     while  (!setOfVolToCheck.empty())
12108     {
12109       std::set<int>::iterator it = setOfVolToCheck.begin();
12110       int vtkId = *it;
12111       if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12112       {
12113         //MESSAGE("volume to recheck,  vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12114         int countInside = 0;
12115         int neighborsVtkIds[NBMAXNEIGHBORS];
12116         int downIds[NBMAXNEIGHBORS];
12117         unsigned char downTypes[NBMAXNEIGHBORS];
12118         int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12119         for (int n = 0; n < nbNeighbors; n++)
12120           if (setOfInsideVol.count(neighborsVtkIds[n]))
12121             countInside++;
12122         //MESSAGE("countInside " << countInside);
12123         if (countInside > 1)
12124         {
12125           //MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12126           setOfInsideVol.insert(vtkId);
12127           sgrp->Add(meshDS->FromVtkToSmds(vtkId));
12128           addedInside = true;
12129         }
12130         else
12131           setOfVolToReCheck.insert(vtkId);
12132       }
12133       setOfVolToCheck.erase(vtkId);
12134     }
12135   }
12136
12137   // --- map of Downward faces at the boundary, inside the global volume
12138   //     map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12139   //     fill group of SMDS faces inside the volume (when several volume shapes)
12140   //     fill group of SMDS faces on the skin of the global volume (if skin)
12141
12142   std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12143   std::map<DownIdType, int, DownIdCompare> skinFaces;     // faces on the skin of the global volume --> corresponding cell
12144   std::set<int>::iterator it = setOfInsideVol.begin();
12145   for (; it != setOfInsideVol.end(); ++it)
12146   {
12147     int vtkId = *it;
12148     //MESSAGE("  vtkId " << vtkId  << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12149     int neighborsVtkIds[NBMAXNEIGHBORS];
12150     int downIds[NBMAXNEIGHBORS];
12151     unsigned char downTypes[NBMAXNEIGHBORS];
12152     int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12153     for (int n = 0; n < nbNeighbors; n++)
12154     {
12155       int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12156       if (neighborDim == 3)
12157       {
12158         if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12159         {
12160           DownIdType face(downIds[n], downTypes[n]);
12161           boundaryFaces[face] = vtkId;
12162         }
12163         // if the face between to volumes is in the mesh, get it (internal face between shapes)
12164         int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12165         if (vtkFaceId >= 0)
12166         {
12167           sgrpi->Add(meshDS->FromVtkToSmds(vtkFaceId));
12168           // find also the smds edges on this face
12169           int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12170           const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12171           const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12172           for (int i = 0; i < nbEdges; i++)
12173           {
12174             int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12175             if (vtkEdgeId >= 0)
12176               sgrpei->Add(meshDS->FromVtkToSmds(vtkEdgeId));
12177           }
12178         }
12179       }
12180       else if (neighborDim == 2) // skin of the volume
12181       {
12182         DownIdType face(downIds[n], downTypes[n]);
12183         skinFaces[face] = vtkId;
12184         int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12185         if (vtkFaceId >= 0)
12186           sgrps->Add(meshDS->FromVtkToSmds(vtkFaceId));
12187       }
12188     }
12189   }
12190
12191   // --- identify the edges constituting the wire of each subshape on the skin
12192   //     define polylines with the nodes of edges, equivalent to wires
12193   //     project polylines on subshapes, and partition, to get geom faces
12194
12195   std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12196   std::set<int> emptySet;
12197   emptySet.clear();
12198   std::set<int> shapeIds;
12199
12200   SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12201   while (itelem->more())
12202   {
12203     const SMDS_MeshElement *elem = itelem->next();
12204     int shapeId = elem->getshapeId();
12205     int   vtkId = elem->GetVtkID();
12206     if (!shapeIdToVtkIdSet.count(shapeId))
12207     {
12208       shapeIdToVtkIdSet[shapeId] = emptySet;
12209       shapeIds.insert(shapeId);
12210     }
12211     shapeIdToVtkIdSet[shapeId].insert(vtkId);
12212   }
12213
12214   std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12215   std::set<DownIdType, DownIdCompare> emptyEdges;
12216   emptyEdges.clear();
12217
12218   std::map<int, std::set<int> >::iterator itShape =  shapeIdToVtkIdSet.begin();
12219   for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12220   {
12221     int shapeId = itShape->first;
12222     //MESSAGE(" --- Shape ID --- "<< shapeId);
12223     shapeIdToEdges[shapeId] = emptyEdges;
12224
12225     std::vector<int> nodesEdges;
12226
12227     std::set<int>::iterator its = itShape->second.begin();
12228     for (; its != itShape->second.end(); ++its)
12229     {
12230       int vtkId = *its;
12231       //MESSAGE("     " << vtkId);
12232       int neighborsVtkIds[NBMAXNEIGHBORS];
12233       int downIds[NBMAXNEIGHBORS];
12234       unsigned char downTypes[NBMAXNEIGHBORS];
12235       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12236       for (int n = 0; n < nbNeighbors; n++)
12237       {
12238         if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12239           continue;
12240         int smdsId = meshDS->FromVtkToSmds(neighborsVtkIds[n]);
12241         const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12242         if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12243         {
12244           DownIdType edge(downIds[n], downTypes[n]);
12245           if (!shapeIdToEdges[shapeId].count(edge))
12246           {
12247             shapeIdToEdges[shapeId].insert(edge);
12248             int vtkNodeId[3];
12249             int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12250             nodesEdges.push_back(vtkNodeId[0]);
12251             nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12252             //MESSAGE("       --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12253           }
12254         }
12255       }
12256     }
12257
12258     std::list<int> order;
12259     order.clear();
12260     if (nodesEdges.size() > 0)
12261     {
12262       order.push_back(nodesEdges[0]); //MESSAGE("       --- back " << order.back()+1); // SMDS id = VTK id + 1;
12263       nodesEdges[0] = -1;
12264       order.push_back(nodesEdges[1]); //MESSAGE("       --- back " << order.back()+1);
12265       nodesEdges[1] = -1; // do not reuse this edge
12266       bool found = true;
12267       while (found)
12268       {
12269         int nodeTofind = order.back(); // try first to push back
12270         int i = 0;
12271         for ( i = 0; i < (int)nodesEdges.size(); i++ )
12272           if (nodesEdges[i] == nodeTofind)
12273             break;
12274         if ( i == (int) nodesEdges.size() )
12275           found = false; // no follower found on back
12276         else
12277         {
12278           if (i%2) // odd ==> use the previous one
12279             if (nodesEdges[i-1] < 0)
12280               found = false;
12281             else
12282             {
12283               order.push_back(nodesEdges[i-1]); //MESSAGE("       --- back " << order.back()+1);
12284               nodesEdges[i-1] = -1;
12285             }
12286           else // even ==> use the next one
12287             if (nodesEdges[i+1] < 0)
12288               found = false;
12289             else
12290             {
12291               order.push_back(nodesEdges[i+1]); //MESSAGE("       --- back " << order.back()+1);
12292               nodesEdges[i+1] = -1;
12293             }
12294         }
12295         if (found)
12296           continue;
12297         // try to push front
12298         found = true;
12299         nodeTofind = order.front(); // try to push front
12300         for ( i = 0;  i < (int)nodesEdges.size(); i++ )
12301           if ( nodesEdges[i] == nodeTofind )
12302             break;
12303         if ( i == (int)nodesEdges.size() )
12304         {
12305           found = false; // no predecessor found on front
12306           continue;
12307         }
12308         if (i%2) // odd ==> use the previous one
12309           if (nodesEdges[i-1] < 0)
12310             found = false;
12311           else
12312           {
12313             order.push_front(nodesEdges[i-1]); //MESSAGE("       --- front " << order.front()+1);
12314             nodesEdges[i-1] = -1;
12315           }
12316         else // even ==> use the next one
12317           if (nodesEdges[i+1] < 0)
12318             found = false;
12319           else
12320           {
12321             order.push_front(nodesEdges[i+1]); //MESSAGE("       --- front " << order.front()+1);
12322             nodesEdges[i+1] = -1;
12323           }
12324       }
12325     }
12326
12327
12328     std::vector<int> nodes;
12329     nodes.push_back(shapeId);
12330     std::list<int>::iterator itl = order.begin();
12331     for (; itl != order.end(); itl++)
12332     {
12333       nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12334       //MESSAGE("              ordered node " << nodes[nodes.size()-1]);
12335     }
12336     listOfListOfNodes.push_back(nodes);
12337   }
12338
12339   //     partition geom faces with blocFissure
12340   //     mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12341   //     mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12342
12343   return;
12344 }
12345
12346
12347 //================================================================================
12348 /*!
12349  * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12350  * The created 2D mesh elements based on nodes of free faces of boundary volumes
12351  * \return TRUE if operation has been completed successfully, FALSE otherwise
12352  */
12353 //================================================================================
12354
12355 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12356 {
12357   // iterates on volume elements and detect all free faces on them
12358   SMESHDS_Mesh* aMesh = GetMeshDS();
12359   if (!aMesh)
12360     return false;
12361
12362   ElemFeatures faceType( SMDSAbs_Face );
12363   int nbFree = 0, nbExisted = 0, nbCreated = 0;
12364   SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12365   while(vIt->more())
12366   {
12367     const SMDS_MeshVolume* volume = vIt->next();
12368     SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12369     vTool.SetExternalNormal();
12370     const int iQuad = volume->IsQuadratic();
12371     faceType.SetQuad( iQuad );
12372     for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12373     {
12374       if (!vTool.IsFreeFace(iface))
12375         continue;
12376       nbFree++;
12377       vector<const SMDS_MeshNode *> nodes;
12378       int nbFaceNodes = vTool.NbFaceNodes(iface);
12379       const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12380       int inode = 0;
12381       for ( ; inode < nbFaceNodes; inode += iQuad+1)
12382         nodes.push_back(faceNodes[inode]);
12383
12384       if (iQuad) // add medium nodes
12385       {
12386         for ( inode = 1; inode < nbFaceNodes; inode += 2)
12387           nodes.push_back(faceNodes[inode]);
12388         if ( nbFaceNodes == 9 ) // bi-quadratic quad
12389           nodes.push_back(faceNodes[8]);
12390       }
12391       // add new face based on volume nodes
12392       if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) )
12393       {
12394         nbExisted++; // face already exists
12395       }
12396       else
12397       {
12398         AddElement( nodes, faceType.SetPoly( nbFaceNodes/(iQuad+1) > 4 ));
12399         nbCreated++;
12400       }
12401     }
12402   }
12403   return ( nbFree == ( nbExisted + nbCreated ));
12404 }
12405
12406 namespace
12407 {
12408   inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12409   {
12410     if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12411       return n;
12412     return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12413   }
12414 }
12415 //================================================================================
12416 /*!
12417  * \brief Creates missing boundary elements
12418  *  \param elements - elements whose boundary is to be checked
12419  *  \param dimension - defines type of boundary elements to create
12420  *  \param group - a group to store created boundary elements in
12421  *  \param targetMesh - a mesh to store created boundary elements in
12422  *  \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12423  *  \param toCopyExistingBoundary - if true, not only new but also pre-existing
12424  *                                boundary elements will be copied into the targetMesh
12425  *  \param toAddExistingBondary - if true, not only new but also pre-existing
12426  *                                boundary elements will be added into the new group
12427  *  \param aroundElements - if true, elements will be created on boundary of given
12428  *                          elements else, on boundary of the whole mesh.
12429  * \return nb of added boundary elements
12430  */
12431 //================================================================================
12432
12433 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12434                                        Bnd_Dimension           dimension,
12435                                        SMESH_Group*            group/*=0*/,
12436                                        SMESH_Mesh*             targetMesh/*=0*/,
12437                                        bool                    toCopyElements/*=false*/,
12438                                        bool                    toCopyExistingBoundary/*=false*/,
12439                                        bool                    toAddExistingBondary/*= false*/,
12440                                        bool                    aroundElements/*= false*/)
12441 {
12442   SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12443   SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12444   // hope that all elements are of the same type, do not check them all
12445   if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12446     throw SALOME_Exception(LOCALIZED("wrong element type"));
12447
12448   if ( !targetMesh )
12449     toCopyElements = toCopyExistingBoundary = false;
12450
12451   SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12452   SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12453   int nbAddedBnd = 0;
12454
12455   // editor adding present bnd elements and optionally holding elements to add to the group
12456   SMESH_MeshEditor* presentEditor;
12457   SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12458   presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12459
12460   SMESH_MesherHelper helper( *myMesh );
12461   const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12462   SMDS_VolumeTool vTool;
12463   TIDSortedElemSet avoidSet;
12464   const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12465   size_t inode;
12466
12467   typedef vector<const SMDS_MeshNode*> TConnectivity;
12468   TConnectivity tgtNodes;
12469   ElemFeatures elemKind( missType ), elemToCopy;
12470
12471   vector<const SMDS_MeshElement*> presentBndElems;
12472   vector<TConnectivity>           missingBndElems;
12473   vector<int>                     freeFacets;
12474   TConnectivity nodes, elemNodes;
12475
12476   SMDS_ElemIteratorPtr eIt;
12477   if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12478   else                  eIt = SMESHUtils::elemSetIterator( elements );
12479
12480   while ( eIt->more() )
12481   {
12482     const SMDS_MeshElement* elem = eIt->next();
12483     const int              iQuad = elem->IsQuadratic();
12484     elemKind.SetQuad( iQuad );
12485
12486     // ------------------------------------------------------------------------------------
12487     // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12488     // ------------------------------------------------------------------------------------
12489     presentBndElems.clear();
12490     missingBndElems.clear();
12491     freeFacets.clear(); nodes.clear(); elemNodes.clear();
12492     if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12493     {
12494       const SMDS_MeshElement* otherVol = 0;
12495       for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12496       {
12497         if ( !vTool.IsFreeFace(iface, &otherVol) &&
12498              ( !aroundElements || elements.count( otherVol )))
12499           continue;
12500         freeFacets.push_back( iface );
12501       }
12502       if ( missType == SMDSAbs_Face )
12503         vTool.SetExternalNormal();
12504       for ( size_t i = 0; i < freeFacets.size(); ++i )
12505       {
12506         int                iface = freeFacets[i];
12507         const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12508         const size_t nbFaceNodes = vTool.NbFaceNodes (iface);
12509         if ( missType == SMDSAbs_Edge ) // boundary edges
12510         {
12511           nodes.resize( 2+iQuad );
12512           for ( size_t i = 0; i < nbFaceNodes; i += 1+iQuad )
12513           {
12514             for ( size_t j = 0; j < nodes.size(); ++j )
12515               nodes[ j ] = nn[ i+j ];
12516             if ( const SMDS_MeshElement* edge =
12517                  aMesh->FindElement( nodes, SMDSAbs_Edge, /*noMedium=*/false ))
12518               presentBndElems.push_back( edge );
12519             else
12520               missingBndElems.push_back( nodes );
12521           }
12522         }
12523         else // boundary face
12524         {
12525           nodes.clear();
12526           for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12527             nodes.push_back( nn[inode] ); // add corner nodes
12528           if (iQuad)
12529             for ( inode = 1; inode < nbFaceNodes; inode += 2)
12530               nodes.push_back( nn[inode] ); // add medium nodes
12531           int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12532           if ( iCenter > 0 )
12533             nodes.push_back( vTool.GetNodes()[ iCenter ] );
12534
12535           if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12536                                                                SMDSAbs_Face, /*noMedium=*/false ))
12537             presentBndElems.push_back( f );
12538           else
12539             missingBndElems.push_back( nodes );
12540
12541           if ( targetMesh != myMesh )
12542           {
12543             // add 1D elements on face boundary to be added to a new mesh
12544             const SMDS_MeshElement* edge;
12545             for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12546             {
12547               if ( iQuad )
12548                 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12549               else
12550                 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12551               if ( edge && avoidSet.insert( edge ).second )
12552                 presentBndElems.push_back( edge );
12553             }
12554           }
12555         }
12556       }
12557     }
12558     else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12559     {
12560       avoidSet.clear(), avoidSet.insert( elem );
12561       elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesIterator() ),
12562                         SMDS_MeshElement::iterator() );
12563       elemNodes.push_back( elemNodes[0] );
12564       nodes.resize( 2 + iQuad );
12565       const int nbLinks = elem->NbCornerNodes();
12566       for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12567       {
12568         nodes[0] = elemNodes[iN];
12569         nodes[1] = elemNodes[iN+1+iQuad];
12570         if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12571           continue; // not free link
12572
12573         if ( iQuad ) nodes[2] = elemNodes[iN+1];
12574         if ( const SMDS_MeshElement* edge =
12575              aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12576           presentBndElems.push_back( edge );
12577         else
12578           missingBndElems.push_back( nodes );
12579       }
12580     }
12581
12582     // ---------------------------------
12583     // 2. Add missing boundary elements
12584     // ---------------------------------
12585     if ( targetMesh != myMesh )
12586       // instead of making a map of nodes in this mesh and targetMesh,
12587       // we create nodes with same IDs.
12588       for ( size_t i = 0; i < missingBndElems.size(); ++i )
12589       {
12590         TConnectivity& srcNodes = missingBndElems[i];
12591         tgtNodes.resize( srcNodes.size() );
12592         for ( inode = 0; inode < srcNodes.size(); ++inode )
12593           tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12594         if ( /*aroundElements && */tgtEditor.GetMeshDS()->FindElement( tgtNodes,
12595                                                                        missType,
12596                                                                        /*noMedium=*/false))
12597           continue;
12598         tgtEditor.AddElement( tgtNodes, elemKind.SetPoly( tgtNodes.size()/(iQuad+1) > 4 ));
12599         ++nbAddedBnd;
12600       }
12601     else
12602       for ( size_t i = 0; i < missingBndElems.size(); ++i )
12603       {
12604         TConnectivity& nodes = missingBndElems[ i ];
12605         if ( /*aroundElements && */tgtEditor.GetMeshDS()->FindElement( nodes,
12606                                                                        missType,
12607                                                                        /*noMedium=*/false))
12608           continue;
12609         SMDS_MeshElement* newElem =
12610           tgtEditor.AddElement( nodes, elemKind.SetPoly( nodes.size()/(iQuad+1) > 4 ));
12611         nbAddedBnd += bool( newElem );
12612
12613         // try to set a new element to a shape
12614         if ( myMesh->HasShapeToMesh() )
12615         {
12616           bool ok = true;
12617           set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12618           const size_t nbN = nodes.size() / (iQuad+1 );
12619           for ( inode = 0; inode < nbN && ok; ++inode )
12620           {
12621             pair<int, TopAbs_ShapeEnum> i_stype =
12622               helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12623             if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12624               mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12625           }
12626           if ( ok && mediumShapes.size() > 1 )
12627           {
12628             set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12629             pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12630             for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12631             {
12632               if (( ok = ( stype_i->first != stype_i_0.first )))
12633                 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12634                                         aMesh->IndexToShape( stype_i_0.second ));
12635             }
12636           }
12637           if ( ok && mediumShapes.begin()->first == missShapeType )
12638             aMesh->SetMeshElementOnShape( newElem, mediumShapes.begin()->second );
12639         }
12640       }
12641
12642     // ----------------------------------
12643     // 3. Copy present boundary elements
12644     // ----------------------------------
12645     if ( toCopyExistingBoundary )
12646       for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12647       {
12648         const SMDS_MeshElement* e = presentBndElems[i];
12649         tgtNodes.resize( e->NbNodes() );
12650         for ( inode = 0; inode < tgtNodes.size(); ++inode )
12651           tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12652         presentEditor->AddElement( tgtNodes, elemToCopy.Init( e ));
12653       }
12654     else // store present elements to add them to a group
12655       for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12656       {
12657         presentEditor->myLastCreatedElems.push_back( presentBndElems[ i ]);
12658       }
12659
12660   } // loop on given elements
12661
12662   // ---------------------------------------------
12663   // 4. Fill group with boundary elements
12664   // ---------------------------------------------
12665   if ( group )
12666   {
12667     if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12668       for ( size_t i = 0; i < tgtEditor.myLastCreatedElems.size(); ++i )
12669         g->SMDSGroup().Add( tgtEditor.myLastCreatedElems[ i ]);
12670   }
12671   tgtEditor.myLastCreatedElems.clear();
12672   tgtEditor2.myLastCreatedElems.clear();
12673
12674   // -----------------------
12675   // 5. Copy given elements
12676   // -----------------------
12677   if ( toCopyElements && targetMesh != myMesh )
12678   {
12679     if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12680     else                  eIt = SMESHUtils::elemSetIterator( elements );
12681     while (eIt->more())
12682     {
12683       const SMDS_MeshElement* elem = eIt->next();
12684       tgtNodes.resize( elem->NbNodes() );
12685       for ( inode = 0; inode < tgtNodes.size(); ++inode )
12686         tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12687       tgtEditor.AddElement( tgtNodes, elemToCopy.Init( elem ));
12688
12689       tgtEditor.myLastCreatedElems.clear();
12690     }
12691   }
12692   return nbAddedBnd;
12693 }
12694
12695 //================================================================================
12696 /*!
12697  * \brief Copy node position and set \a to node on the same geometry
12698  */
12699 //================================================================================
12700
12701 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
12702                                      const SMDS_MeshNode* to )
12703 {
12704   if ( !from || !to ) return;
12705
12706   SMDS_PositionPtr pos = from->GetPosition();
12707   if ( !pos || from->getshapeId() < 1 ) return;
12708
12709   switch ( pos->GetTypeOfPosition() )
12710   {
12711   case SMDS_TOP_3DSPACE: break;
12712
12713   case SMDS_TOP_FACE:
12714   {
12715     SMDS_FacePositionPtr fPos = pos;
12716     GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
12717                                 fPos->GetUParameter(), fPos->GetVParameter() );
12718     break;
12719   }
12720   case SMDS_TOP_EDGE:
12721   {
12722     // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
12723     SMDS_EdgePositionPtr ePos = pos;
12724     GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
12725     break;
12726   }
12727   case SMDS_TOP_VERTEX:
12728   {
12729     GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
12730     break;
12731   }
12732   case SMDS_TOP_UNSPEC:
12733   default:;
12734   }
12735 }