Salome HOME
2104c79201a169a71d80d56986caf70d78b988c9
[modules/smesh.git] / src / SMESH / SMESH_MeshEditor.cxx
1 // Copyright (C) 2007-2019  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 == 20) {
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],node[15],
304                                                  node[16],node[17],node[18],node[19],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],node[15],
309                                                  node[16],node[17],node[18],node[19] );
310       }
311       else if (nbnode == 27) {
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],
317                                                  node[20],node[21],node[22],node[23],
318                                                  node[24],node[25],node[26], ID);
319         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
320                                                  node[4], node[5], node[6], node[7],
321                                                  node[8], node[9], node[10],node[11],
322                                                  node[12],node[13],node[14],node[15],
323                                                  node[16],node[17],node[18],node[19],
324                                                  node[20],node[21],node[22],node[23],
325                                                  node[24],node[25],node[26] );
326       }
327     }
328     else if ( !features.myIsQuad )
329     {
330       if ( ID >= 1 ) e = mesh->AddPolyhedralVolumeWithID(node, features.myPolyhedQuantities, ID);
331       else           e = mesh->AddPolyhedralVolume      (node, features.myPolyhedQuantities    );
332     }
333     else
334     {
335       // if ( ID >= 1 ) e = mesh->AddQuadPolyhedralVolumeWithID(node, features.myPolyhedQuantities,ID);
336       // else           e = mesh->AddQuadPolyhedralVolume      (node, features.myPolyhedQuantities   );
337     }
338     break;
339
340   case SMDSAbs_Edge:
341     if ( nbnode == 2 ) {
342       if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], ID);
343       else           e = mesh->AddEdge      (node[0], node[1] );
344     }
345     else if ( nbnode == 3 ) {
346       if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], node[2], ID);
347       else           e = mesh->AddEdge      (node[0], node[1], node[2] );
348     }
349     break;
350
351   case SMDSAbs_0DElement:
352     if ( nbnode == 1 ) {
353       if ( ID >= 1 ) e = mesh->Add0DElementWithID(node[0], ID);
354       else           e = mesh->Add0DElement      (node[0] );
355     }
356     break;
357
358   case SMDSAbs_Node:
359     if ( ID >= 1 ) e = mesh->AddNodeWithID(node[0]->X(), node[0]->Y(), node[0]->Z(), ID);
360     else           e = mesh->AddNode      (node[0]->X(), node[0]->Y(), node[0]->Z()    );
361     break;
362
363   case SMDSAbs_Ball:
364     if ( ID >= 1 ) e = mesh->AddBallWithID(node[0], features.myBallDiameter, ID);
365     else           e = mesh->AddBall      (node[0], features.myBallDiameter    );
366     break;
367
368   default:;
369   }
370   if ( e ) myLastCreatedElems.push_back( e );
371   return e;
372 }
373
374 //=======================================================================
375 /*!
376  * \brief Add element
377  */
378 //=======================================================================
379
380 SMDS_MeshElement* SMESH_MeshEditor::AddElement(const vector<int> & nodeIDs,
381                                                const ElemFeatures& features)
382 {
383   vector<const SMDS_MeshNode*> nodes;
384   nodes.reserve( nodeIDs.size() );
385   vector<int>::const_iterator id = nodeIDs.begin();
386   while ( id != nodeIDs.end() ) {
387     if ( const SMDS_MeshNode* node = GetMeshDS()->FindNode( *id++ ))
388       nodes.push_back( node );
389     else
390       return 0;
391   }
392   return AddElement( nodes, features );
393 }
394
395 //=======================================================================
396 //function : Remove
397 //purpose  : Remove a node or an element.
398 //           Modify a compute state of sub-meshes which become empty
399 //=======================================================================
400
401 int SMESH_MeshEditor::Remove (const list< int >& theIDs,
402                               const bool         isNodes )
403 {
404   ClearLastCreated();
405
406   SMESHDS_Mesh* aMesh = GetMeshDS();
407   set< SMESH_subMesh *> smmap;
408
409   int removed = 0;
410   list<int>::const_iterator it = theIDs.begin();
411   for ( ; it != theIDs.end(); it++ ) {
412     const SMDS_MeshElement * elem;
413     if ( isNodes )
414       elem = aMesh->FindNode( *it );
415     else
416       elem = aMesh->FindElement( *it );
417     if ( !elem )
418       continue;
419
420     // Notify VERTEX sub-meshes about modification
421     if ( isNodes ) {
422       const SMDS_MeshNode* node = cast2Node( elem );
423       if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX )
424         if ( int aShapeID = node->getshapeId() )
425           if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
426             smmap.insert( sm );
427     }
428     // Find sub-meshes to notify about modification
429     //     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
430     //     while ( nodeIt->more() ) {
431     //       const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
432     //       const SMDS_PositionPtr& aPosition = node->GetPosition();
433     //       if ( aPosition.get() ) {
434     //         if ( int aShapeID = aPosition->GetShapeId() ) {
435     //           if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
436     //             smmap.insert( sm );
437     //         }
438     //       }
439     //     }
440
441     // Do remove
442     if ( isNodes )
443       aMesh->RemoveNode( static_cast< const SMDS_MeshNode* >( elem ));
444     else
445       aMesh->RemoveElement( elem );
446     removed++;
447   }
448
449   // Notify sub-meshes about modification
450   if ( !smmap.empty() ) {
451     set< SMESH_subMesh *>::iterator smIt;
452     for ( smIt = smmap.begin(); smIt != smmap.end(); smIt++ )
453       (*smIt)->ComputeStateEngine( SMESH_subMesh::MESH_ENTITY_REMOVED );
454   }
455
456   //   // Check if the whole mesh becomes empty
457   //   if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( 1 ) )
458   //     sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
459
460   return removed;
461 }
462
463 //================================================================================
464 /*!
465  * \brief Create 0D elements on all nodes of the given object.
466  *  \param elements - Elements on whose nodes to create 0D elements; if empty, 
467  *                    the all mesh is treated
468  *  \param all0DElems - returns all 0D elements found or created on nodes of \a elements
469  *  \param duplicateElements - to add one more 0D element to a node or not
470  */
471 //================================================================================
472
473 void SMESH_MeshEditor::Create0DElementsOnAllNodes( const TIDSortedElemSet& elements,
474                                                    TIDSortedElemSet&       all0DElems,
475                                                    const bool              duplicateElements )
476 {
477   SMDS_ElemIteratorPtr elemIt;
478   if ( elements.empty() )
479   {
480     elemIt = GetMeshDS()->elementsIterator( SMDSAbs_Node );
481   }
482   else
483   {
484     elemIt = SMESHUtils::elemSetIterator( elements );
485   }
486
487   while ( elemIt->more() )
488   {
489     const SMDS_MeshElement* e = elemIt->next();
490     SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
491     while ( nodeIt->more() )
492     {
493       const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
494       SMDS_ElemIteratorPtr it0D = n->GetInverseElementIterator( SMDSAbs_0DElement );
495       if ( duplicateElements || !it0D->more() )
496       {
497         myLastCreatedElems.push_back( GetMeshDS()->Add0DElement( n ));
498         all0DElems.insert( myLastCreatedElems.back() );
499       }
500       while ( it0D->more() )
501         all0DElems.insert( it0D->next() );
502     }
503   }
504 }
505
506 //=======================================================================
507 //function : FindShape
508 //purpose  : Return an index of the shape theElem is on
509 //           or zero if a shape not found
510 //=======================================================================
511
512 int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem)
513 {
514   ClearLastCreated();
515
516   SMESHDS_Mesh * aMesh = GetMeshDS();
517   if ( aMesh->ShapeToMesh().IsNull() )
518     return 0;
519
520   int aShapeID = theElem->getshapeId();
521   if ( aShapeID < 1 )
522     return 0;
523
524   if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ))
525     if ( sm->Contains( theElem ))
526       return aShapeID;
527
528   if ( theElem->GetType() == SMDSAbs_Node ) {
529     MESSAGE( ":( Error: invalid myShapeId of node " << theElem->GetID() );
530   }
531   else {
532     MESSAGE( ":( Error: invalid myShapeId of element " << theElem->GetID() );
533   }
534
535   TopoDS_Shape aShape; // the shape a node of theElem is on
536   if ( theElem->GetType() != SMDSAbs_Node )
537   {
538     SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
539     while ( nodeIt->more() ) {
540       const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
541       if ((aShapeID = node->getshapeId()) > 0) {
542         if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ) ) {
543           if ( sm->Contains( theElem ))
544             return aShapeID;
545           if ( aShape.IsNull() )
546             aShape = aMesh->IndexToShape( aShapeID );
547         }
548       }
549     }
550   }
551
552   // None of nodes is on a proper shape,
553   // find the shape among ancestors of aShape on which a node is
554   if ( !aShape.IsNull() ) {
555     TopTools_ListIteratorOfListOfShape ancIt( GetMesh()->GetAncestors( aShape ));
556     for ( ; ancIt.More(); ancIt.Next() ) {
557       SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() );
558       if ( sm && sm->Contains( theElem ))
559         return aMesh->ShapeToIndex( ancIt.Value() );
560     }
561   }
562   else
563   {
564     SMESHDS_SubMeshIteratorPtr smIt = GetMeshDS()->SubMeshes();
565     while ( const SMESHDS_SubMesh* sm = smIt->next() )
566       if ( sm->Contains( theElem ))
567         return sm->GetID();
568   }
569
570   return 0;
571 }
572
573 //=======================================================================
574 //function : IsMedium
575 //purpose  :
576 //=======================================================================
577
578 bool SMESH_MeshEditor::IsMedium(const SMDS_MeshNode*      node,
579                                 const SMDSAbs_ElementType typeToCheck)
580 {
581   bool isMedium = false;
582   SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(typeToCheck);
583   while (it->more() && !isMedium ) {
584     const SMDS_MeshElement* elem = it->next();
585     isMedium = elem->IsMediumNode(node);
586   }
587   return isMedium;
588 }
589
590 //=======================================================================
591 //function : shiftNodesQuadTria
592 //purpose  : Shift nodes in the array corresponded to quadratic triangle
593 //           example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
594 //=======================================================================
595
596 static void shiftNodesQuadTria(vector< const SMDS_MeshNode* >& aNodes)
597 {
598   const SMDS_MeshNode* nd1 = aNodes[0];
599   aNodes[0] = aNodes[1];
600   aNodes[1] = aNodes[2];
601   aNodes[2] = nd1;
602   const SMDS_MeshNode* nd2 = aNodes[3];
603   aNodes[3] = aNodes[4];
604   aNodes[4] = aNodes[5];
605   aNodes[5] = nd2;
606 }
607
608 //=======================================================================
609 //function : getNodesFromTwoTria
610 //purpose  : 
611 //=======================================================================
612
613 static bool getNodesFromTwoTria(const SMDS_MeshElement * theTria1,
614                                 const SMDS_MeshElement * theTria2,
615                                 vector< const SMDS_MeshNode*>& N1,
616                                 vector< const SMDS_MeshNode*>& N2)
617 {
618   N1.assign( theTria1->begin_nodes(), theTria1->end_nodes() );
619   if ( N1.size() < 6 ) return false;
620   N2.assign( theTria2->begin_nodes(), theTria2->end_nodes() );
621   if ( N2.size() < 6 ) return false;
622
623   int sames[3] = {-1,-1,-1};
624   int nbsames = 0;
625   int i, j;
626   for(i=0; i<3; i++) {
627     for(j=0; j<3; j++) {
628       if(N1[i]==N2[j]) {
629         sames[i] = j;
630         nbsames++;
631         break;
632       }
633     }
634   }
635   if(nbsames!=2) return false;
636   if(sames[0]>-1) {
637     shiftNodesQuadTria(N1);
638     if(sames[1]>-1) {
639       shiftNodesQuadTria(N1);
640     }
641   }
642   i = sames[0] + sames[1] + sames[2];
643   for(; i<2; i++) {
644     shiftNodesQuadTria(N2);
645   }
646   // now we receive following N1 and N2 (using numeration as in the image below)
647   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
648   // i.e. first nodes from both arrays form a new diagonal
649   return true;
650 }
651
652 //=======================================================================
653 //function : InverseDiag
654 //purpose  : Replace two neighbour triangles with ones built on the same 4 nodes
655 //           but having other common link.
656 //           Return False if args are improper
657 //=======================================================================
658
659 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1,
660                                     const SMDS_MeshElement * theTria2 )
661 {
662   ClearLastCreated();
663
664   if ( !theTria1 || !theTria2 ||
665        !dynamic_cast<const SMDS_MeshCell*>( theTria1 ) ||
666        !dynamic_cast<const SMDS_MeshCell*>( theTria2 ) ||
667        theTria1->GetType() != SMDSAbs_Face ||
668        theTria2->GetType() != SMDSAbs_Face )
669     return false;
670
671   if ((theTria1->GetEntityType() == SMDSEntity_Triangle) &&
672       (theTria2->GetEntityType() == SMDSEntity_Triangle))
673   {
674     //  1 +--+ A  theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
675     //    | /|    theTria2: ( B A 2 ) B->1 ( 1 A 2 )   |\ |
676     //    |/ |                                         | \|
677     //  B +--+ 2                                     B +--+ 2
678
679     // put nodes in array and find out indices of the same ones
680     const SMDS_MeshNode* aNodes [6];
681     int sameInd [] = { -1, -1, -1, -1, -1, -1 };
682     int i = 0;
683     SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
684     while ( it->more() ) {
685       aNodes[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
686
687       if ( i > 2 ) // theTria2
688         // find same node of theTria1
689         for ( int j = 0; j < 3; j++ )
690           if ( aNodes[ i ] == aNodes[ j ]) {
691             sameInd[ j ] = i;
692             sameInd[ i ] = j;
693             break;
694           }
695       // next
696       i++;
697       if ( i == 3 ) {
698         if ( it->more() )
699           return false; // theTria1 is not a triangle
700         it = theTria2->nodesIterator();
701       }
702       if ( i == 6 && it->more() )
703         return false; // theTria2 is not a triangle
704     }
705
706     // find indices of 1,2 and of A,B in theTria1
707     int iA = -1, iB = 0, i1 = 0, i2 = 0;
708     for ( i = 0; i < 6; i++ ) {
709       if ( sameInd [ i ] == -1 ) {
710         if ( i < 3 ) i1 = i;
711         else         i2 = i;
712       }
713       else if (i < 3) {
714         if ( iA >= 0) iB = i;
715         else          iA = i;
716       }
717     }
718     // nodes 1 and 2 should not be the same
719     if ( aNodes[ i1 ] == aNodes[ i2 ] )
720       return false;
721
722     // theTria1: A->2
723     aNodes[ iA ] = aNodes[ i2 ];
724     // theTria2: B->1
725     aNodes[ sameInd[ iB ]] = aNodes[ i1 ];
726
727     GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 );
728     GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 );
729
730     return true;
731
732   } // end if(F1 && F2)
733
734   // check case of quadratic faces
735   if (theTria1->GetEntityType() != SMDSEntity_Quad_Triangle &&
736       theTria1->GetEntityType() != SMDSEntity_BiQuad_Triangle)
737     return false;
738   if (theTria2->GetEntityType() != SMDSEntity_Quad_Triangle&&
739       theTria2->GetEntityType() != SMDSEntity_BiQuad_Triangle)
740     return false;
741
742   //       5
743   //  1 +--+--+ 2  theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
744   //    |    /|    theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
745   //    |   / |
746   //  7 +  +  + 6
747   //    | /9  |
748   //    |/    |
749   //  4 +--+--+ 3
750   //       8
751
752   vector< const SMDS_MeshNode* > N1;
753   vector< const SMDS_MeshNode* > N2;
754   if(!getNodesFromTwoTria(theTria1,theTria2,N1,N2))
755     return false;
756   // now we receive following N1 and N2 (using numeration as above image)
757   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
758   // i.e. first nodes from both arrays determ new diagonal
759
760   vector< const SMDS_MeshNode*> N1new( N1.size() );
761   vector< const SMDS_MeshNode*> N2new( N2.size() );
762   N1new.back() = N1.back(); // central node of biquadratic
763   N2new.back() = N2.back();
764   N1new[0] = N1[0];  N2new[0] = N1[0];
765   N1new[1] = N2[0];  N2new[1] = N1[1];
766   N1new[2] = N2[1];  N2new[2] = N2[0];
767   N1new[3] = N1[4];  N2new[3] = N1[3];
768   N1new[4] = N2[3];  N2new[4] = N2[5];
769   N1new[5] = N1[5];  N2new[5] = N1[4];
770   // change nodes in faces
771   GetMeshDS()->ChangeElementNodes( theTria1, &N1new[0], N1new.size() );
772   GetMeshDS()->ChangeElementNodes( theTria2, &N2new[0], N2new.size() );
773
774   // move the central node of biquadratic triangle
775   SMESH_MesherHelper helper( *GetMesh() );
776   for ( int is2nd = 0; is2nd < 2; ++is2nd )
777   {
778     const SMDS_MeshElement*         tria = is2nd ? theTria2 : theTria1;
779     vector< const SMDS_MeshNode*>& nodes = is2nd ? N2new : N1new;
780     if ( nodes.size() < 7 )
781       continue;
782     helper.SetSubShape( tria->getshapeId() );
783     const TopoDS_Face& F = TopoDS::Face( helper.GetSubShape() );
784     gp_Pnt xyz;
785     if ( F.IsNull() )
786     {
787       xyz = ( SMESH_NodeXYZ( nodes[3] ) +
788               SMESH_NodeXYZ( nodes[4] ) +
789               SMESH_NodeXYZ( nodes[5] )) / 3.;
790     }
791     else
792     {
793       bool checkUV;
794       gp_XY uv = ( helper.GetNodeUV( F, nodes[3], nodes[2], &checkUV ) +
795                    helper.GetNodeUV( F, nodes[4], nodes[0], &checkUV ) +
796                    helper.GetNodeUV( F, nodes[5], nodes[1], &checkUV )) / 3.;
797       TopLoc_Location loc;
798       Handle(Geom_Surface) S = BRep_Tool::Surface(F,loc);
799       xyz = S->Value( uv.X(), uv.Y() );
800       xyz.Transform( loc );
801       if ( nodes[6]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE &&  // set UV
802            nodes[6]->getshapeId() > 0 )
803         GetMeshDS()->SetNodeOnFace( nodes[6], nodes[6]->getshapeId(), uv.X(), uv.Y() );
804     }
805     GetMeshDS()->MoveNode( nodes[6], xyz.X(), xyz.Y(), xyz.Z() );
806   }
807   return true;
808 }
809
810 //=======================================================================
811 //function : findTriangles
812 //purpose  : find triangles sharing theNode1-theNode2 link
813 //=======================================================================
814
815 static bool findTriangles(const SMDS_MeshNode *    theNode1,
816                           const SMDS_MeshNode *    theNode2,
817                           const SMDS_MeshElement*& theTria1,
818                           const SMDS_MeshElement*& theTria2)
819 {
820   if ( !theNode1 || !theNode2 ) return false;
821
822   theTria1 = theTria2 = 0;
823
824   set< const SMDS_MeshElement* > emap;
825   SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face);
826   while (it->more()) {
827     const SMDS_MeshElement* elem = it->next();
828     if ( elem->NbCornerNodes() == 3 )
829       emap.insert( elem );
830   }
831   it = theNode2->GetInverseElementIterator(SMDSAbs_Face);
832   while (it->more()) {
833     const SMDS_MeshElement* elem = it->next();
834     if ( emap.count( elem )) {
835       if ( !theTria1 )
836       {
837         theTria1 = elem;
838       }
839       else  
840       {
841         theTria2 = elem;
842         // theTria1 must be element with minimum ID
843         if ( theTria2->GetID() < theTria1->GetID() )
844           std::swap( theTria2, theTria1 );
845         return true;
846       }
847     }
848   }
849   return false;
850 }
851
852 //=======================================================================
853 //function : InverseDiag
854 //purpose  : Replace two neighbour triangles sharing theNode1-theNode2 link
855 //           with ones built on the same 4 nodes but having other common link.
856 //           Return false if proper faces not found
857 //=======================================================================
858
859 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
860                                     const SMDS_MeshNode * theNode2)
861 {
862   ClearLastCreated();
863
864   const SMDS_MeshElement *tr1, *tr2;
865   if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
866     return false;
867
868   if ( !dynamic_cast<const SMDS_MeshCell*>( tr1 ) ||
869        !dynamic_cast<const SMDS_MeshCell*>( tr2 ))
870     return false;
871
872   if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
873       (tr2->GetEntityType() == SMDSEntity_Triangle)) {
874
875     //  1 +--+ A  tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
876     //    | /|    tr2: ( B A 2 ) B->1 ( 1 A 2 )   |\ |
877     //    |/ |                                    | \|
878     //  B +--+ 2                                B +--+ 2
879
880     // put nodes in array
881     // and find indices of 1,2 and of A in tr1 and of B in tr2
882     int i, iA1 = 0, i1 = 0;
883     const SMDS_MeshNode* aNodes1 [3];
884     SMDS_ElemIteratorPtr it;
885     for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) {
886       aNodes1[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
887       if ( aNodes1[ i ] == theNode1 )
888         iA1 = i; // node A in tr1
889       else if ( aNodes1[ i ] != theNode2 )
890         i1 = i;  // node 1
891     }
892     int iB2 = 0, i2 = 0;
893     const SMDS_MeshNode* aNodes2 [3];
894     for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) {
895       aNodes2[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
896       if ( aNodes2[ i ] == theNode2 )
897         iB2 = i; // node B in tr2
898       else if ( aNodes2[ i ] != theNode1 )
899         i2 = i;  // node 2
900     }
901
902     // nodes 1 and 2 should not be the same
903     if ( aNodes1[ i1 ] == aNodes2[ i2 ] )
904       return false;
905
906     // tr1: A->2
907     aNodes1[ iA1 ] = aNodes2[ i2 ];
908     // tr2: B->1
909     aNodes2[ iB2 ] = aNodes1[ i1 ];
910
911     GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 );
912     GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 );
913
914     return true;
915   }
916
917   // check case of quadratic faces
918   return InverseDiag(tr1,tr2);
919 }
920
921 //=======================================================================
922 //function : getQuadrangleNodes
923 //purpose  : fill theQuadNodes - nodes of a quadrangle resulting from
924 //           fusion of triangles tr1 and tr2 having shared link on
925 //           theNode1 and theNode2
926 //=======================================================================
927
928 bool getQuadrangleNodes(const SMDS_MeshNode *    theQuadNodes [],
929                         const SMDS_MeshNode *    theNode1,
930                         const SMDS_MeshNode *    theNode2,
931                         const SMDS_MeshElement * tr1,
932                         const SMDS_MeshElement * tr2 )
933 {
934   if( tr1->NbNodes() != tr2->NbNodes() )
935     return false;
936   // find the 4-th node to insert into tr1
937   const SMDS_MeshNode* n4 = 0;
938   SMDS_ElemIteratorPtr it = tr2->nodesIterator();
939   int i=0;
940   while ( !n4 && i<3 ) {
941     const SMDS_MeshNode * n = cast2Node( it->next() );
942     i++;
943     bool isDiag = ( n == theNode1 || n == theNode2 );
944     if ( !isDiag )
945       n4 = n;
946   }
947   // Make an array of nodes to be in a quadrangle
948   int iNode = 0, iFirstDiag = -1;
949   it = tr1->nodesIterator();
950   i=0;
951   while ( i<3 ) {
952     const SMDS_MeshNode * n = cast2Node( it->next() );
953     i++;
954     bool isDiag = ( n == theNode1 || n == theNode2 );
955     if ( isDiag ) {
956       if ( iFirstDiag < 0 )
957         iFirstDiag = iNode;
958       else if ( iNode - iFirstDiag == 1 )
959         theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes
960     }
961     else if ( n == n4 ) {
962       return false; // tr1 and tr2 should not have all the same nodes
963     }
964     theQuadNodes[ iNode++ ] = n;
965   }
966   if ( iNode == 3 ) // diagonal nodes have 0 and 2 indices
967     theQuadNodes[ iNode ] = n4;
968
969   return true;
970 }
971
972 //=======================================================================
973 //function : DeleteDiag
974 //purpose  : Replace two neighbour triangles sharing theNode1-theNode2 link
975 //           with a quadrangle built on the same 4 nodes.
976 //           Return false if proper faces not found
977 //=======================================================================
978
979 bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
980                                    const SMDS_MeshNode * theNode2)
981 {
982   ClearLastCreated();
983
984   const SMDS_MeshElement *tr1, *tr2;
985   if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
986     return false;
987
988   if ( !dynamic_cast<const SMDS_MeshCell*>( tr1 ) ||
989        !dynamic_cast<const SMDS_MeshCell*>( tr2 ))
990     return false;
991
992   SMESHDS_Mesh * aMesh = GetMeshDS();
993
994   if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
995       (tr2->GetEntityType() == SMDSEntity_Triangle))
996   {
997     const SMDS_MeshNode* aNodes [ 4 ];
998     if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 ))
999       return false;
1000
1001     const SMDS_MeshElement* newElem = 0;
1002     newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3] );
1003     myLastCreatedElems.push_back(newElem);
1004     AddToSameGroups( newElem, tr1, aMesh );
1005     int aShapeId = tr1->getshapeId();
1006     if ( aShapeId )
1007       aMesh->SetMeshElementOnShape( newElem, aShapeId );
1008
1009     aMesh->RemoveElement( tr1 );
1010     aMesh->RemoveElement( tr2 );
1011
1012     return true;
1013   }
1014
1015   // check case of quadratic faces
1016   if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
1017     return false;
1018   if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
1019     return false;
1020
1021   //       5
1022   //  1 +--+--+ 2  tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
1023   //    |    /|    tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
1024   //    |   / |
1025   //  7 +  +  + 6
1026   //    | /9  |
1027   //    |/    |
1028   //  4 +--+--+ 3
1029   //       8
1030
1031   vector< const SMDS_MeshNode* > N1;
1032   vector< const SMDS_MeshNode* > N2;
1033   if(!getNodesFromTwoTria(tr1,tr2,N1,N2))
1034     return false;
1035   // now we receive following N1 and N2 (using numeration as above image)
1036   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
1037   // i.e. first nodes from both arrays determ new diagonal
1038
1039   const SMDS_MeshNode* aNodes[8];
1040   aNodes[0] = N1[0];
1041   aNodes[1] = N1[1];
1042   aNodes[2] = N2[0];
1043   aNodes[3] = N2[1];
1044   aNodes[4] = N1[3];
1045   aNodes[5] = N2[5];
1046   aNodes[6] = N2[3];
1047   aNodes[7] = N1[5];
1048
1049   const SMDS_MeshElement* newElem = 0;
1050   newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
1051                             aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
1052   myLastCreatedElems.push_back(newElem);
1053   AddToSameGroups( newElem, tr1, aMesh );
1054   int aShapeId = tr1->getshapeId();
1055   if ( aShapeId )
1056   {
1057     aMesh->SetMeshElementOnShape( newElem, aShapeId );
1058   }
1059   aMesh->RemoveElement( tr1 );
1060   aMesh->RemoveElement( tr2 );
1061
1062   // remove middle node (9)
1063   GetMeshDS()->RemoveNode( N1[4] );
1064
1065   return true;
1066 }
1067
1068 //=======================================================================
1069 //function : Reorient
1070 //purpose  : Reverse theElement orientation
1071 //=======================================================================
1072
1073 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
1074 {
1075   ClearLastCreated();
1076
1077   if (!theElem)
1078     return false;
1079   SMDS_ElemIteratorPtr it = theElem->nodesIterator();
1080   if ( !it || !it->more() )
1081     return false;
1082
1083   const SMDSAbs_ElementType type = theElem->GetType();
1084   if ( type < SMDSAbs_Edge || type > SMDSAbs_Volume )
1085     return false;
1086
1087   const SMDSAbs_EntityType geomType = theElem->GetEntityType();
1088   if ( geomType == SMDSEntity_Polyhedra ) // polyhedron
1089   {
1090     const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( theElem );
1091     if (!aPolyedre) {
1092       MESSAGE("Warning: bad volumic element");
1093       return false;
1094     }
1095     SMDS_VolumeTool vTool( aPolyedre );
1096     const int nbFaces = vTool.NbFaces();
1097     vector<int> quantities( nbFaces );
1098     vector<const SMDS_MeshNode *> poly_nodes;
1099
1100     // check if all facets are oriented equally
1101     bool sameOri = true;
1102     vector<int>& facetOri = quantities; // keep orientation in quantities so far
1103     for (int iface = 0; iface < nbFaces; iface++)
1104     {
1105       facetOri[ iface ] = vTool.IsFaceExternal( iface );
1106       if ( facetOri[ iface ] != facetOri[ 0 ])
1107         sameOri = false;
1108     }
1109
1110     // reverse faces of the polyhedron
1111     int neededOri = sameOri ? 1 - facetOri[0] : 1;
1112     poly_nodes.reserve( vTool.NbNodes() );
1113     for ( int iface = 0; iface < nbFaces; iface++ )
1114     {
1115       int             nbFaceNodes = vTool.NbFaceNodes( iface );
1116       const SMDS_MeshNode** nodes = vTool.GetFaceNodes( iface );
1117       bool toReverse = ( facetOri[ iface ] != neededOri );
1118
1119       quantities[ iface ] = nbFaceNodes;
1120
1121       if ( toReverse )
1122         for ( int inode = nbFaceNodes - 1; inode >= 0; inode-- )
1123           poly_nodes.push_back( nodes[ inode ]);
1124       else
1125         poly_nodes.insert( poly_nodes.end(), nodes, nodes + nbFaceNodes );
1126     }
1127     return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
1128   }
1129   else // other elements
1130   {
1131     vector<const SMDS_MeshNode*> nodes( theElem->begin_nodes(), theElem->end_nodes() );
1132     const std::vector<int>& interlace = SMDS_MeshCell::reverseSmdsOrder( geomType, nodes.size() );
1133     if ( interlace.empty() )
1134     {
1135       std::reverse( nodes.begin(), nodes.end() ); // obsolete, just in case
1136     }
1137     else
1138     {
1139       SMDS_MeshCell::applyInterlace( interlace, nodes );
1140     }
1141     return GetMeshDS()->ChangeElementNodes( theElem, &nodes[0], nodes.size() );
1142   }
1143   return false;
1144 }
1145
1146 //================================================================================
1147 /*!
1148  * \brief Reorient faces.
1149  * \param theFaces - the faces to reorient. If empty the whole mesh is meant
1150  * \param theDirection - desired direction of normal of \a theFace
1151  * \param theFace - one of \a theFaces that should be oriented according to
1152  *        \a theDirection and whose orientation defines orientation of other faces
1153  * \return number of reoriented faces.
1154  */
1155 //================================================================================
1156
1157 int SMESH_MeshEditor::Reorient2D (TIDSortedElemSet &       theFaces,
1158                                   const gp_Dir&            theDirection,
1159                                   const SMDS_MeshElement * theFace)
1160 {
1161   int nbReori = 0;
1162   if ( !theFace || theFace->GetType() != SMDSAbs_Face ) return nbReori;
1163
1164   if ( theFaces.empty() )
1165   {
1166     SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator(/*idInceasingOrder=true*/);
1167     while ( fIt->more() )
1168       theFaces.insert( theFaces.end(), fIt->next() );
1169   }
1170
1171   // orient theFace according to theDirection
1172   gp_XYZ normal;
1173   SMESH_MeshAlgos::FaceNormal( theFace, normal, /*normalized=*/false );
1174   if ( normal * theDirection.XYZ() < 0 )
1175     nbReori += Reorient( theFace );
1176
1177   // Orient other faces
1178
1179   set< const SMDS_MeshElement* > startFaces, visitedFaces;
1180   TIDSortedElemSet avoidSet;
1181   set< SMESH_TLink > checkedLinks;
1182   pair< set< SMESH_TLink >::iterator, bool > linkIt_isNew;
1183
1184   if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces
1185     theFaces.erase( theFace );
1186   startFaces.insert( theFace );
1187
1188   int nodeInd1, nodeInd2;
1189   const SMDS_MeshElement*           otherFace;
1190   vector< const SMDS_MeshElement* > facesNearLink;
1191   vector< std::pair< int, int > >   nodeIndsOfFace;
1192
1193   set< const SMDS_MeshElement* >::iterator startFace = startFaces.begin();
1194   while ( !startFaces.empty() )
1195   {
1196     startFace = startFaces.begin();
1197     theFace = *startFace;
1198     startFaces.erase( startFace );
1199     if ( !visitedFaces.insert( theFace ).second )
1200       continue;
1201
1202     avoidSet.clear();
1203     avoidSet.insert(theFace);
1204
1205     NLink link( theFace->GetNode( 0 ), (SMDS_MeshNode *) 0 );
1206
1207     const int nbNodes = theFace->NbCornerNodes();
1208     for ( int i = 0; i < nbNodes; ++i ) // loop on links of theFace
1209     {
1210       link.second = theFace->GetNode(( i+1 ) % nbNodes );
1211       linkIt_isNew = checkedLinks.insert( link );
1212       if ( !linkIt_isNew.second )
1213       {
1214         // link has already been checked and won't be encountered more
1215         // if the group (theFaces) is manifold
1216         //checkedLinks.erase( linkIt_isNew.first );
1217       }
1218       else
1219       {
1220         facesNearLink.clear();
1221         nodeIndsOfFace.clear();
1222         while (( otherFace = SMESH_MeshAlgos::FindFaceInSet( link.first, link.second,
1223                                                              theFaces, avoidSet,
1224                                                              &nodeInd1, &nodeInd2 )))
1225           if ( otherFace != theFace)
1226           {
1227             facesNearLink.push_back( otherFace );
1228             nodeIndsOfFace.push_back( make_pair( nodeInd1, nodeInd2 ));
1229             avoidSet.insert( otherFace );
1230           }
1231         if ( facesNearLink.size() > 1 )
1232         {
1233           // NON-MANIFOLD mesh shell !
1234           // select a face most co-directed with theFace,
1235           // other faces won't be visited this time
1236           gp_XYZ NF, NOF;
1237           SMESH_MeshAlgos::FaceNormal( theFace, NF, /*normalized=*/false );
1238           double proj, maxProj = -1;
1239           for ( size_t i = 0; i < facesNearLink.size(); ++i ) {
1240             SMESH_MeshAlgos::FaceNormal( facesNearLink[i], NOF, /*normalized=*/false );
1241             if (( proj = Abs( NF * NOF )) > maxProj ) {
1242               maxProj = proj;
1243               otherFace = facesNearLink[i];
1244               nodeInd1  = nodeIndsOfFace[i].first;
1245               nodeInd2  = nodeIndsOfFace[i].second;
1246             }
1247           }
1248           // not to visit rejected faces
1249           for ( size_t i = 0; i < facesNearLink.size(); ++i )
1250             if ( facesNearLink[i] != otherFace && theFaces.size() > 1 )
1251               visitedFaces.insert( facesNearLink[i] );
1252         }
1253         else if ( facesNearLink.size() == 1 )
1254         {
1255           otherFace = facesNearLink[0];
1256           nodeInd1  = nodeIndsOfFace.back().first;
1257           nodeInd2  = nodeIndsOfFace.back().second;
1258         }
1259         if ( otherFace && otherFace != theFace)
1260         {
1261           // link must be reverse in otherFace if orientation to otherFace
1262           // is same as that of theFace
1263           if ( abs(nodeInd2-nodeInd1) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1264           {
1265             nbReori += Reorient( otherFace );
1266           }
1267           startFaces.insert( otherFace );
1268         }
1269       }
1270       std::swap( link.first, link.second ); // reverse the link
1271     }
1272   }
1273   return nbReori;
1274 }
1275
1276 //================================================================================
1277 /*!
1278  * \brief Reorient faces basing on orientation of adjacent volumes.
1279  * \param theFaces - faces to reorient. If empty, all mesh faces are treated.
1280  * \param theVolumes - reference volumes.
1281  * \param theOutsideNormal - to orient faces to have their normal
1282  *        pointing either \a outside or \a inside the adjacent volumes.
1283  * \return number of reoriented faces.
1284  */
1285 //================================================================================
1286
1287 int SMESH_MeshEditor::Reorient2DBy3D (TIDSortedElemSet & theFaces,
1288                                       TIDSortedElemSet & theVolumes,
1289                                       const bool         theOutsideNormal)
1290 {
1291   int nbReori = 0;
1292
1293   SMDS_ElemIteratorPtr faceIt;
1294   if ( theFaces.empty() )
1295     faceIt = GetMeshDS()->elementsIterator( SMDSAbs_Face );
1296   else
1297     faceIt = SMESHUtils::elemSetIterator( theFaces );
1298
1299   vector< const SMDS_MeshNode* > faceNodes;
1300   TIDSortedElemSet checkedVolumes;
1301   set< const SMDS_MeshNode* > faceNodesSet;
1302   SMDS_VolumeTool volumeTool;
1303
1304   while ( faceIt->more() ) // loop on given faces
1305   {
1306     const SMDS_MeshElement* face = faceIt->next();
1307     if ( face->GetType() != SMDSAbs_Face )
1308       continue;
1309
1310     const size_t nbCornersNodes = face->NbCornerNodes();
1311     faceNodes.assign( face->begin_nodes(), face->end_nodes() );
1312
1313     checkedVolumes.clear();
1314     SMDS_ElemIteratorPtr vIt = faceNodes[ 0 ]->GetInverseElementIterator( SMDSAbs_Volume );
1315     while ( vIt->more() )
1316     {
1317       const SMDS_MeshElement* volume = vIt->next();
1318
1319       if ( !checkedVolumes.insert( volume ).second )
1320         continue;
1321       if ( !theVolumes.empty() && !theVolumes.count( volume ))
1322         continue;
1323
1324       // is volume adjacent?
1325       bool allNodesCommon = true;
1326       for ( size_t iN = 1; iN < nbCornersNodes && allNodesCommon; ++iN )
1327         allNodesCommon = ( volume->GetNodeIndex( faceNodes[ iN ]) > -1 );
1328       if ( !allNodesCommon )
1329         continue;
1330
1331       // get nodes of a corresponding volume facet
1332       faceNodesSet.clear();
1333       faceNodesSet.insert( faceNodes.begin(), faceNodes.end() );
1334       volumeTool.Set( volume );
1335       int facetID = volumeTool.GetFaceIndex( faceNodesSet );
1336       if ( facetID < 0 ) continue;
1337       volumeTool.SetExternalNormal();
1338       const SMDS_MeshNode** facetNodes = volumeTool.GetFaceNodes( facetID );
1339
1340       // compare order of faceNodes and facetNodes
1341       const int iQ = 1 + ( nbCornersNodes < faceNodes.size() );
1342       int iNN[2];
1343       for ( int i = 0; i < 2; ++i )
1344       {
1345         const SMDS_MeshNode* n = facetNodes[ i*iQ ];
1346         for ( size_t iN = 0; iN < nbCornersNodes; ++iN )
1347           if ( faceNodes[ iN ] == n )
1348           {
1349             iNN[ i ] = iN;
1350             break;
1351           }
1352       }
1353       bool isOutside = Abs( iNN[0]-iNN[1] ) == 1 ? iNN[0] < iNN[1] : iNN[0] > iNN[1];
1354       if ( isOutside != theOutsideNormal )
1355         nbReori += Reorient( face );
1356     }
1357   }  // loop on given faces
1358
1359   return nbReori;
1360 }
1361
1362 //=======================================================================
1363 //function : getBadRate
1364 //purpose  :
1365 //=======================================================================
1366
1367 static double getBadRate (const SMDS_MeshElement*               theElem,
1368                           SMESH::Controls::NumericalFunctorPtr& theCrit)
1369 {
1370   SMESH::Controls::TSequenceOfXYZ P;
1371   if ( !theElem || !theCrit->GetPoints( theElem, P ))
1372     return 1e100;
1373   return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1374   //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1375 }
1376
1377 //=======================================================================
1378 //function : QuadToTri
1379 //purpose  : Cut quadrangles into triangles.
1380 //           theCrit is used to select a diagonal to cut
1381 //=======================================================================
1382
1383 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet &                   theElems,
1384                                   SMESH::Controls::NumericalFunctorPtr theCrit)
1385 {
1386   ClearLastCreated();
1387
1388   if ( !theCrit.get() )
1389     return false;
1390
1391   SMESHDS_Mesh *       aMesh = GetMeshDS();
1392   Handle(Geom_Surface) surface;
1393   SMESH_MesherHelper   helper( *GetMesh() );
1394
1395   myLastCreatedElems.reserve( theElems.size() * 2 );
1396
1397   TIDSortedElemSet::iterator itElem;
1398   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
1399   {
1400     const SMDS_MeshElement* elem = *itElem;
1401     if ( !elem || elem->GetType() != SMDSAbs_Face )
1402       continue;
1403     if ( elem->NbCornerNodes() != 4 )
1404       continue;
1405
1406     // retrieve element nodes
1407     vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1408
1409     // compare two sets of possible triangles
1410     double aBadRate1, aBadRate2; // to what extent a set is bad
1411     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1412     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1413     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1414
1415     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1416     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1417     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1418
1419     const int aShapeId = FindShape( elem );
1420     const SMDS_MeshElement* newElem1 = 0;
1421     const SMDS_MeshElement* newElem2 = 0;
1422
1423     if ( !elem->IsQuadratic() ) // split linear quadrangle
1424     {
1425       // for MaxElementLength2D functor we return minimum diagonal for splitting,
1426       // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1427       if ( aBadRate1 <= aBadRate2 ) {
1428         // tr1 + tr2 is better
1429         newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1430         newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1431       }
1432       else {
1433         // tr3 + tr4 is better
1434         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1435         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1436       }
1437     }
1438     else // split quadratic quadrangle
1439     {
1440       helper.SetIsQuadratic( true );
1441       helper.SetIsBiQuadratic( aNodes.size() == 9 );
1442
1443       helper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem ));
1444       if ( aNodes.size() == 9 )
1445       {
1446         helper.SetIsBiQuadratic( true );
1447         if ( aBadRate1 <= aBadRate2 )
1448           helper.AddTLinkNode( aNodes[0], aNodes[2], aNodes[8] );
1449         else
1450           helper.AddTLinkNode( aNodes[1], aNodes[3], aNodes[8] );
1451       }
1452       // create a new element
1453       if ( aBadRate1 <= aBadRate2 ) {
1454         newElem1 = helper.AddFace( aNodes[2], aNodes[3], aNodes[0] );
1455         newElem2 = helper.AddFace( aNodes[2], aNodes[0], aNodes[1] );
1456       }
1457       else {
1458         newElem1 = helper.AddFace( aNodes[3], aNodes[0], aNodes[1] );
1459         newElem2 = helper.AddFace( aNodes[3], aNodes[1], aNodes[2] );
1460       }
1461     } // quadratic case
1462
1463     // care of a new element
1464
1465     myLastCreatedElems.push_back(newElem1);
1466     myLastCreatedElems.push_back(newElem2);
1467     AddToSameGroups( newElem1, elem, aMesh );
1468     AddToSameGroups( newElem2, elem, aMesh );
1469
1470     // put a new triangle on the same shape
1471     if ( aShapeId )
1472       aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1473     aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1474
1475     aMesh->RemoveElement( elem );
1476   }
1477   return true;
1478 }
1479
1480 //=======================================================================
1481 /*!
1482  * \brief Split each of given quadrangles into 4 triangles.
1483  * \param theElems - The faces to be split. If empty all faces are split.
1484  */
1485 //=======================================================================
1486
1487 void SMESH_MeshEditor::QuadTo4Tri (TIDSortedElemSet & theElems)
1488 {
1489   ClearLastCreated();
1490   myLastCreatedElems.reserve( theElems.size() * 4 );
1491
1492   SMESH_MesherHelper helper( *GetMesh() );
1493   helper.SetElementsOnShape( true );
1494
1495   SMDS_ElemIteratorPtr faceIt;
1496   if ( theElems.empty() ) faceIt = GetMeshDS()->elementsIterator(SMDSAbs_Face);
1497   else                    faceIt = SMESHUtils::elemSetIterator( theElems );
1498
1499   bool   checkUV;
1500   gp_XY  uv [9]; uv[8] = gp_XY(0,0);
1501   gp_XYZ xyz[9];
1502   vector< const SMDS_MeshNode* > nodes;
1503   SMESHDS_SubMesh*               subMeshDS = 0;
1504   TopoDS_Face                    F;
1505   Handle(Geom_Surface)           surface;
1506   TopLoc_Location                loc;
1507
1508   while ( faceIt->more() )
1509   {
1510     const SMDS_MeshElement* quad = faceIt->next();
1511     if ( !quad || quad->NbCornerNodes() != 4 )
1512       continue;
1513
1514     // get a surface the quad is on
1515
1516     if ( quad->getshapeId() < 1 )
1517     {
1518       F.Nullify();
1519       helper.SetSubShape( 0 );
1520       subMeshDS = 0;
1521     }
1522     else if ( quad->getshapeId() != helper.GetSubShapeID() )
1523     {
1524       helper.SetSubShape( quad->getshapeId() );
1525       if ( !helper.GetSubShape().IsNull() &&
1526            helper.GetSubShape().ShapeType() == TopAbs_FACE )
1527       {
1528         F = TopoDS::Face( helper.GetSubShape() );
1529         surface = BRep_Tool::Surface( F, loc );
1530         subMeshDS = GetMeshDS()->MeshElements( quad->getshapeId() );
1531       }
1532       else
1533       {
1534         helper.SetSubShape( 0 );
1535         subMeshDS = 0;
1536       }
1537     }
1538
1539     // create a central node
1540
1541     const SMDS_MeshNode* nCentral;
1542     nodes.assign( quad->begin_nodes(), quad->end_nodes() );
1543
1544     if ( nodes.size() == 9 )
1545     {
1546       nCentral = nodes.back();
1547     }
1548     else
1549     {
1550       size_t iN = 0;
1551       if ( F.IsNull() )
1552       {
1553         for ( ; iN < nodes.size(); ++iN )
1554           xyz[ iN ] = SMESH_NodeXYZ( nodes[ iN ] );
1555
1556         for ( ; iN < 8; ++iN ) // mid-side points of a linear qudrangle
1557           xyz[ iN ] = 0.5 * ( xyz[ iN - 4 ] + xyz[( iN - 3 )%4 ] );
1558
1559         xyz[ 8 ] = helper.calcTFI( 0.5, 0.5,
1560                                    xyz[0], xyz[1], xyz[2], xyz[3],
1561                                    xyz[4], xyz[5], xyz[6], xyz[7] );
1562       }
1563       else
1564       {
1565         for ( ; iN < nodes.size(); ++iN )
1566           uv[ iN ] = helper.GetNodeUV( F, nodes[iN], nodes[(iN+2)%4], &checkUV );
1567
1568         for ( ; iN < 8; ++iN ) // UV of mid-side points of a linear qudrangle
1569           uv[ iN ] = helper.GetMiddleUV( surface, uv[ iN - 4 ], uv[( iN - 3 )%4 ] );
1570
1571         uv[ 8 ] = helper.calcTFI( 0.5, 0.5,
1572                                   uv[0], uv[1], uv[2], uv[3],
1573                                   uv[4], uv[5], uv[6], uv[7] );
1574
1575         gp_Pnt p = surface->Value( uv[8].X(), uv[8].Y() ).Transformed( loc );
1576         xyz[ 8 ] = p.XYZ();
1577       }
1578
1579       nCentral = helper.AddNode( xyz[8].X(), xyz[8].Y(), xyz[8].Z(), /*id=*/0,
1580                                  uv[8].X(), uv[8].Y() );
1581       myLastCreatedNodes.push_back( nCentral );
1582     }
1583
1584     // create 4 triangles
1585
1586     helper.SetIsQuadratic  ( nodes.size() > 4 );
1587     helper.SetIsBiQuadratic( nodes.size() == 9 );
1588     if ( helper.GetIsQuadratic() )
1589       helper.AddTLinks( static_cast< const SMDS_MeshFace*>( quad ));
1590
1591     GetMeshDS()->RemoveFreeElement( quad, subMeshDS, /*fromGroups=*/false );
1592
1593     for ( int i = 0; i < 4; ++i )
1594     {
1595       SMDS_MeshElement* tria = helper.AddFace( nodes[ i ],
1596                                                nodes[(i+1)%4],
1597                                                nCentral );
1598       ReplaceElemInGroups( tria, quad, GetMeshDS() );
1599       myLastCreatedElems.push_back( tria );
1600     }
1601   }
1602 }
1603
1604 //=======================================================================
1605 //function : BestSplit
1606 //purpose  : Find better diagonal for cutting.
1607 //=======================================================================
1608
1609 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement*              theQuad,
1610                                  SMESH::Controls::NumericalFunctorPtr theCrit)
1611 {
1612   ClearLastCreated();
1613
1614   if (!theCrit.get())
1615     return -1;
1616
1617   if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1618     return -1;
1619
1620   if( theQuad->NbNodes()==4 ||
1621       (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1622
1623     // retrieve element nodes
1624     const SMDS_MeshNode* aNodes [4];
1625     SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1626     int i = 0;
1627     //while (itN->more())
1628     while (i<4) {
1629       aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1630     }
1631     // compare two sets of possible triangles
1632     double aBadRate1, aBadRate2; // to what extent a set is bad
1633     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1634     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1635     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1636
1637     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1638     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1639     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1640     // for MaxElementLength2D functor we return minimum diagonal for splitting,
1641     // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1642     if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1643       return 1; // diagonal 1-3
1644
1645     return 2; // diagonal 2-4
1646   }
1647   return -1;
1648 }
1649
1650 namespace
1651 {
1652   // Methods of splitting volumes into tetra
1653
1654   const int theHexTo5_1[5*4+1] =
1655     {
1656       0, 1, 2, 5,    0, 4, 5, 7,     0, 2, 3, 7,    2, 5, 6, 7,     0, 5, 2, 7,   -1
1657     };
1658   const int theHexTo5_2[5*4+1] =
1659     {
1660       1, 2, 3, 6,    1, 4, 5, 6,     0, 1, 3, 4,    3, 4, 6, 7,     1, 3, 4, 6,   -1
1661     };
1662   const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1663
1664   const int theHexTo6_1[6*4+1] =
1665     {
1666       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
1667     };
1668   const int theHexTo6_2[6*4+1] =
1669     {
1670       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
1671     };
1672   const int theHexTo6_3[6*4+1] =
1673     {
1674       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
1675     };
1676   const int theHexTo6_4[6*4+1] =
1677     {
1678       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
1679     };
1680   const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1681
1682   const int thePyraTo2_1[2*4+1] =
1683     {
1684       0, 1, 2, 4,    0, 2, 3, 4,   -1
1685     };
1686   const int thePyraTo2_2[2*4+1] =
1687     {
1688       1, 2, 3, 4,    1, 3, 0, 4,   -1
1689     };
1690   const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1691
1692   const int thePentaTo3_1[3*4+1] =
1693     {
1694       0, 1, 2, 3,    1, 3, 4, 2,     2, 3, 4, 5,    -1
1695     };
1696   const int thePentaTo3_2[3*4+1] =
1697     {
1698       1, 2, 0, 4,    2, 4, 5, 0,     0, 4, 5, 3,    -1
1699     };
1700   const int thePentaTo3_3[3*4+1] =
1701     {
1702       2, 0, 1, 5,    0, 5, 3, 1,     1, 5, 3, 4,    -1
1703     };
1704   const int thePentaTo3_4[3*4+1] =
1705     {
1706       0, 1, 2, 3,    1, 3, 4, 5,     2, 3, 1, 5,    -1
1707     };
1708   const int thePentaTo3_5[3*4+1] =
1709     {
1710       1, 2, 0, 4,    2, 4, 5, 3,     0, 4, 2, 3,    -1
1711     };
1712   const int thePentaTo3_6[3*4+1] =
1713     {
1714       2, 0, 1, 5,    0, 5, 3, 4,     1, 5, 0, 4,    -1
1715     };
1716   const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1717                                 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1718
1719   // Methods of splitting hexahedron into prisms
1720
1721   const int theHexTo4Prisms_BT[6*4+1] = // bottom-top
1722     {
1723       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
1724     };
1725   const int theHexTo4Prisms_LR[6*4+1] = // left-right
1726     {
1727       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
1728     };
1729   const int theHexTo4Prisms_FB[6*4+1] = // front-back
1730     {
1731       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
1732     };
1733
1734   const int theHexTo2Prisms_BT_1[6*2+1] =
1735     {
1736       0, 1, 3, 4, 5, 7,    1, 2, 3, 5, 6, 7,   -1
1737     };
1738   const int theHexTo2Prisms_BT_2[6*2+1] =
1739     {
1740       0, 1, 2, 4, 5, 6,    0, 2, 3, 4, 6, 7,   -1
1741     };
1742   const int* theHexTo2Prisms_BT[2] = { theHexTo2Prisms_BT_1, theHexTo2Prisms_BT_2 };
1743
1744   const int theHexTo2Prisms_LR_1[6*2+1] =
1745     {
1746       1, 0, 4, 2, 3, 7,    1, 4, 5, 2, 7, 6,   -1
1747     };
1748   const int theHexTo2Prisms_LR_2[6*2+1] =
1749     {
1750       1, 0, 4, 2, 3, 7,    1, 4, 5, 2, 7, 6,   -1
1751     };
1752   const int* theHexTo2Prisms_LR[2] = { theHexTo2Prisms_LR_1, theHexTo2Prisms_LR_2 };
1753
1754   const int theHexTo2Prisms_FB_1[6*2+1] =
1755     {
1756       0, 3, 4, 1, 2, 5,    3, 7, 4, 2, 6, 5,   -1
1757     };
1758   const int theHexTo2Prisms_FB_2[6*2+1] =
1759     {
1760       0, 3, 7, 1, 2, 7,    0, 7, 4, 1, 6, 5,   -1
1761     };
1762   const int* theHexTo2Prisms_FB[2] = { theHexTo2Prisms_FB_1, theHexTo2Prisms_FB_2 };
1763
1764
1765   struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1766   {
1767     int _n1, _n2, _n3;
1768     TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1769     bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1770     bool hasAdjacentVol( const SMDS_MeshElement*    elem,
1771                          const SMDSAbs_GeometryType geom = SMDSGeom_TETRA) const;
1772   };
1773   struct TSplitMethod
1774   {
1775     int        _nbSplits;
1776     int        _nbCorners;
1777     const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1778     bool       _baryNode;     //!< additional node is to be created at cell barycenter
1779     bool       _ownConn;      //!< to delete _connectivity in destructor
1780     map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1781
1782     TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1783       : _nbSplits(nbTet), _nbCorners(4), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1784     ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1785     bool hasFacet( const TTriangleFacet& facet ) const
1786     {
1787       if ( _nbCorners == 4 )
1788       {
1789         const int* tetConn = _connectivity;
1790         for ( ; tetConn[0] >= 0; tetConn += 4 )
1791           if (( facet.contains( tetConn[0] ) +
1792                 facet.contains( tetConn[1] ) +
1793                 facet.contains( tetConn[2] ) +
1794                 facet.contains( tetConn[3] )) == 3 )
1795             return true;
1796       }
1797       else // prism, _nbCorners == 6
1798       {
1799         const int* prismConn = _connectivity;
1800         for ( ; prismConn[0] >= 0; prismConn += 6 )
1801         {
1802           if (( facet.contains( prismConn[0] ) &&
1803                 facet.contains( prismConn[1] ) &&
1804                 facet.contains( prismConn[2] ))
1805               ||
1806               ( facet.contains( prismConn[3] ) &&
1807                 facet.contains( prismConn[4] ) &&
1808                 facet.contains( prismConn[5] )))
1809             return true;
1810         }
1811       }
1812       return false;
1813     }
1814   };
1815
1816   //=======================================================================
1817   /*!
1818    * \brief return TSplitMethod for the given element to split into tetrahedra
1819    */
1820   //=======================================================================
1821
1822   TSplitMethod getTetraSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1823   {
1824     const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1825
1826     // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1827     // an edge and a face barycenter; tertaherdons are based on triangles and
1828     // a volume barycenter
1829     const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1830
1831     // Find out how adjacent volumes are split
1832
1833     vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1834     int hasAdjacentSplits = 0, maxTetConnSize = 0;
1835     for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1836     {
1837       int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1838       maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1839       if ( nbNodes < 4 ) continue;
1840
1841       list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1842       const int* nInd = vol.GetFaceNodesIndices( iF );
1843       if ( nbNodes == 4 )
1844       {
1845         TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1846         TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1847         if      ( t012.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t012 );
1848         else if ( t123.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t123 );
1849       }
1850       else
1851       {
1852         int iCom = 0; // common node of triangle faces to split into
1853         for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1854         {
1855           TTriangleFacet t012( nInd[ iQ * ( iCom             )],
1856                                nInd[ iQ * ( (iCom+1)%nbNodes )],
1857                                nInd[ iQ * ( (iCom+2)%nbNodes )]);
1858           TTriangleFacet t023( nInd[ iQ * ( iCom             )],
1859                                nInd[ iQ * ( (iCom+2)%nbNodes )],
1860                                nInd[ iQ * ( (iCom+3)%nbNodes )]);
1861           if ( t012.hasAdjacentVol( vol.Element() ) && t023.hasAdjacentVol( vol.Element() ))
1862           {
1863             triaSplits.push_back( t012 );
1864             triaSplits.push_back( t023 );
1865             break;
1866           }
1867         }
1868       }
1869       if ( !triaSplits.empty() )
1870         hasAdjacentSplits = true;
1871     }
1872
1873     // Among variants of split method select one compliant with adjacent volumes
1874
1875     TSplitMethod method;
1876     if ( !vol.Element()->IsPoly() && !is24TetMode )
1877     {
1878       int nbVariants = 2, nbTet = 0;
1879       const int** connVariants = 0;
1880       switch ( vol.Element()->GetEntityType() )
1881       {
1882       case SMDSEntity_Hexa:
1883       case SMDSEntity_Quad_Hexa:
1884       case SMDSEntity_TriQuad_Hexa:
1885         if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1886           connVariants = theHexTo5, nbTet = 5;
1887         else
1888           connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1889         break;
1890       case SMDSEntity_Pyramid:
1891       case SMDSEntity_Quad_Pyramid:
1892         connVariants = thePyraTo2;  nbTet = 2;
1893         break;
1894       case SMDSEntity_Penta:
1895       case SMDSEntity_Quad_Penta:
1896       case SMDSEntity_BiQuad_Penta:
1897         connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1898         break;
1899       default:
1900         nbVariants = 0;
1901       }
1902       for ( int variant = 0; variant < nbVariants && method._nbSplits == 0; ++variant )
1903       {
1904         // check method compliancy with adjacent tetras,
1905         // all found splits must be among facets of tetras described by this method
1906         method = TSplitMethod( nbTet, connVariants[variant] );
1907         if ( hasAdjacentSplits && method._nbSplits > 0 )
1908         {
1909           bool facetCreated = true;
1910           for ( size_t iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1911           {
1912             list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1913             for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1914               facetCreated = method.hasFacet( *facet );
1915           }
1916           if ( !facetCreated )
1917             method = TSplitMethod(0); // incompatible method
1918         }
1919       }
1920     }
1921     if ( method._nbSplits < 1 )
1922     {
1923       // No standard method is applicable, use a generic solution:
1924       // each facet of a volume is split into triangles and
1925       // each of triangles and a volume barycenter form a tetrahedron.
1926
1927       const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1928
1929       int* connectivity = new int[ maxTetConnSize + 1 ];
1930       method._connectivity = connectivity;
1931       method._ownConn = true;
1932       method._baryNode = !isHex27; // to create central node or not
1933
1934       int connSize = 0;
1935       int baryCenInd = vol.NbNodes() - int( isHex27 );
1936       for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1937       {
1938         const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1939         const int*   nInd = vol.GetFaceNodesIndices( iF );
1940         // find common node of triangle facets of tetra to create
1941         int iCommon = 0; // index in linear numeration
1942         const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1943         if ( !triaSplits.empty() )
1944         {
1945           // by found facets
1946           const TTriangleFacet* facet = &triaSplits.front();
1947           for ( ; iCommon < nbNodes-1 ; ++iCommon )
1948             if ( facet->contains( nInd[ iQ * iCommon ]) &&
1949                  facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1950               break;
1951         }
1952         else if ( nbNodes > 3 && !is24TetMode )
1953         {
1954           // find the best method of splitting into triangles by aspect ratio
1955           SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1956           map< double, int > badness2iCommon;
1957           const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1958           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1959           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1960           {
1961             double badness = 0;
1962             for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1963             {
1964               SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon         )],
1965                                       nodes[ iQ*((iLast-1)%nbNodes)],
1966                                       nodes[ iQ*((iLast  )%nbNodes)]);
1967               badness += getBadRate( &tria, aspectRatio );
1968             }
1969             badness2iCommon.insert( make_pair( badness, iCommon ));
1970           }
1971           // use iCommon with lowest badness
1972           iCommon = badness2iCommon.begin()->second;
1973         }
1974         if ( iCommon >= nbNodes )
1975           iCommon = 0; // something wrong
1976
1977         // fill connectivity of tetrahedra based on a current face
1978         int nbTet = nbNodes - 2;
1979         if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
1980         {
1981           int faceBaryCenInd;
1982           if ( isHex27 )
1983           {
1984             faceBaryCenInd = vol.GetCenterNodeIndex( iF );
1985             method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
1986           }
1987           else
1988           {
1989             method._faceBaryNode[ iF ] = 0;
1990             faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
1991           }
1992           nbTet = nbNodes;
1993           for ( int i = 0; i < nbTet; ++i )
1994           {
1995             int i1 = i, i2 = (i+1) % nbNodes;
1996             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1997             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1998             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1999             connectivity[ connSize++ ] = faceBaryCenInd;
2000             connectivity[ connSize++ ] = baryCenInd;
2001           }
2002         }
2003         else
2004         {
2005           for ( int i = 0; i < nbTet; ++i )
2006           {
2007             int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
2008             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2009             connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
2010             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2011             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2012             connectivity[ connSize++ ] = baryCenInd;
2013           }
2014         }
2015         method._nbSplits += nbTet;
2016
2017       } // loop on volume faces
2018
2019       connectivity[ connSize++ ] = -1;
2020
2021     } // end of generic solution
2022
2023     return method;
2024   }
2025   //=======================================================================
2026   /*!
2027    * \brief return TSplitMethod to split haxhedron into prisms
2028    */
2029   //=======================================================================
2030
2031   TSplitMethod getPrismSplitMethod( SMDS_VolumeTool& vol,
2032                                     const int        methodFlags,
2033                                     const int        facetToSplit)
2034   {
2035     // order of facets in HEX according to SMDS_VolumeTool::Hexa_F :
2036     // B, T, L, B, R, F
2037     const int iF = ( facetToSplit < 2 ) ? 0 : 1 + ( facetToSplit-2 ) % 2; // [0,1,2]
2038
2039     if ( methodFlags == SMESH_MeshEditor::HEXA_TO_4_PRISMS )
2040     {
2041       static TSplitMethod to4methods[4]; // order BT, LR, FB
2042       if ( to4methods[iF]._nbSplits == 0 )
2043       {
2044         switch ( iF ) {
2045         case 0:
2046           to4methods[iF]._connectivity = theHexTo4Prisms_BT;
2047           to4methods[iF]._faceBaryNode[ 0 ] = 0;
2048           to4methods[iF]._faceBaryNode[ 1 ] = 0;
2049           break;
2050         case 1:
2051           to4methods[iF]._connectivity = theHexTo4Prisms_LR;
2052           to4methods[iF]._faceBaryNode[ 2 ] = 0;
2053           to4methods[iF]._faceBaryNode[ 4 ] = 0;
2054           break;
2055         case 2:
2056           to4methods[iF]._connectivity = theHexTo4Prisms_FB;
2057           to4methods[iF]._faceBaryNode[ 3 ] = 0;
2058           to4methods[iF]._faceBaryNode[ 5 ] = 0;
2059           break;
2060         default: return to4methods[3];
2061         }
2062         to4methods[iF]._nbSplits  = 4;
2063         to4methods[iF]._nbCorners = 6;
2064       }
2065       return to4methods[iF];
2066     }
2067     // else if ( methodFlags == HEXA_TO_2_PRISMS )
2068
2069     TSplitMethod method;
2070
2071     const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2072
2073     const int nbVariants = 2, nbSplits = 2;
2074     const int** connVariants = 0;
2075     switch ( iF ) {
2076     case 0: connVariants = theHexTo2Prisms_BT; break;
2077     case 1: connVariants = theHexTo2Prisms_LR; break;
2078     case 2: connVariants = theHexTo2Prisms_FB; break;
2079     default: return method;
2080     }
2081
2082     // look for prisms adjacent via facetToSplit and an opposite one
2083     for ( int is2nd = 0; is2nd < 2; ++is2nd )
2084     {
2085       int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2086       int nbNodes = vol.NbFaceNodes( iFacet ) / iQ;
2087       if ( nbNodes != 4 ) return method;
2088
2089       const int* nInd = vol.GetFaceNodesIndices( iFacet );
2090       TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2091       TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2092       TTriangleFacet* t;
2093       if      ( t012.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2094         t = &t012;
2095       else if ( t123.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2096         t = &t123;
2097       else
2098         continue;
2099
2100       // there are adjacent prism
2101       for ( int variant = 0; variant < nbVariants; ++variant )
2102       {
2103         // check method compliancy with adjacent prisms,
2104         // the found prism facets must be among facets of prisms described by current method
2105         method._nbSplits     = nbSplits;
2106         method._nbCorners    = 6;
2107         method._connectivity = connVariants[ variant ];
2108         if ( method.hasFacet( *t ))
2109           return method;
2110       }
2111     }
2112
2113     // No adjacent prisms. Select a variant with a best aspect ratio.
2114
2115     double badness[2] = { 0., 0. };
2116     static SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
2117     const SMDS_MeshNode** nodes = vol.GetNodes();
2118     for ( int variant = 0; variant < nbVariants; ++variant )
2119       for ( int is2nd = 0; is2nd < 2; ++is2nd )
2120       {
2121         int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2122         const int*             nInd = vol.GetFaceNodesIndices( iFacet );
2123
2124         method._connectivity = connVariants[ variant ];
2125         TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2126         TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2127         TTriangleFacet* t = ( method.hasFacet( t012 )) ? & t012 : & t123;
2128
2129         SMDS_FaceOfNodes tria ( nodes[ t->_n1 ],
2130                                 nodes[ t->_n2 ],
2131                                 nodes[ t->_n3 ] );
2132         badness[ variant ] += getBadRate( &tria, aspectRatio );
2133       }
2134     const int iBetter = ( badness[1] < badness[0] && badness[0]-badness[1] > 0.1 * badness[0] );
2135
2136     method._nbSplits     = nbSplits;
2137     method._nbCorners    = 6;
2138     method._connectivity = connVariants[ iBetter ];
2139
2140     return method;
2141   }
2142
2143   //================================================================================
2144   /*!
2145    * \brief Check if there is a tetraherdon adjacent to the given element via this facet
2146    */
2147   //================================================================================
2148
2149   bool TTriangleFacet::hasAdjacentVol( const SMDS_MeshElement*    elem,
2150                                        const SMDSAbs_GeometryType geom ) const
2151   {
2152     // find the tetrahedron including the three nodes of facet
2153     const SMDS_MeshNode* n1 = elem->GetNode(_n1);
2154     const SMDS_MeshNode* n2 = elem->GetNode(_n2);
2155     const SMDS_MeshNode* n3 = elem->GetNode(_n3);
2156     SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
2157     while ( volIt1->more() )
2158     {
2159       const SMDS_MeshElement* v = volIt1->next();
2160       if ( v->GetGeomType() != geom )
2161         continue;
2162       const int lastCornerInd = v->NbCornerNodes() - 1;
2163       if ( v->IsQuadratic() && v->GetNodeIndex( n1 ) > lastCornerInd )
2164         continue; // medium node not allowed
2165       const int ind2 = v->GetNodeIndex( n2 );
2166       if ( ind2 < 0 || lastCornerInd < ind2 )
2167         continue;
2168       const int ind3 = v->GetNodeIndex( n3 );
2169       if ( ind3 < 0 || lastCornerInd < ind3 )
2170         continue;
2171       return true;
2172     }
2173     return false;
2174   }
2175
2176   //=======================================================================
2177   /*!
2178    * \brief A key of a face of volume
2179    */
2180   //=======================================================================
2181
2182   struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
2183   {
2184     TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
2185     {
2186       TIDSortedNodeSet sortedNodes;
2187       const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2188       int nbNodes = vol.NbFaceNodes( iF );
2189       const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
2190       for ( int i = 0; i < nbNodes; i += iQ )
2191         sortedNodes.insert( fNodes[i] );
2192       TIDSortedNodeSet::iterator n = sortedNodes.begin();
2193       first.first   = (*(n++))->GetID();
2194       first.second  = (*(n++))->GetID();
2195       second.first  = (*(n++))->GetID();
2196       second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
2197     }
2198   };
2199 } // namespace
2200
2201 //=======================================================================
2202 //function : SplitVolumes
2203 //purpose  : Split volume elements into tetrahedra or prisms.
2204 //           If facet ID < 0, element is split into tetrahedra,
2205 //           else a hexahedron is split into prisms so that the given facet is
2206 //           split into triangles
2207 //=======================================================================
2208
2209 void SMESH_MeshEditor::SplitVolumes (const TFacetOfElem & theElems,
2210                                      const int            theMethodFlags)
2211 {
2212   SMDS_VolumeTool    volTool;
2213   SMESH_MesherHelper helper( *GetMesh()), fHelper(*GetMesh());
2214   fHelper.ToFixNodeParameters( true );
2215
2216   SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
2217   SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
2218
2219   SMESH_SequenceOfElemPtr newNodes, newElems;
2220
2221   // map face of volume to it's baricenrtic node
2222   map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
2223   double bc[3];
2224   vector<const SMDS_MeshElement* > splitVols;
2225
2226   TFacetOfElem::const_iterator elem2facet = theElems.begin();
2227   for ( ; elem2facet != theElems.end(); ++elem2facet )
2228   {
2229     const SMDS_MeshElement* elem = elem2facet->first;
2230     const int       facetToSplit = elem2facet->second;
2231     if ( elem->GetType() != SMDSAbs_Volume )
2232       continue;
2233     const SMDSAbs_EntityType geomType = elem->GetEntityType();
2234     if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
2235       continue;
2236
2237     if ( !volTool.Set( elem, /*ignoreCentralNodes=*/false )) continue; // strange...
2238
2239     TSplitMethod splitMethod = ( facetToSplit < 0  ?
2240                                  getTetraSplitMethod( volTool, theMethodFlags ) :
2241                                  getPrismSplitMethod( volTool, theMethodFlags, facetToSplit ));
2242     if ( splitMethod._nbSplits < 1 ) continue;
2243
2244     // find submesh to add new tetras to
2245     if ( !subMesh || !subMesh->Contains( elem ))
2246     {
2247       int shapeID = FindShape( elem );
2248       helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
2249       subMesh = GetMeshDS()->MeshElements( shapeID );
2250     }
2251     int iQ;
2252     if ( elem->IsQuadratic() )
2253     {
2254       iQ = 2;
2255       // add quadratic links to the helper
2256       for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2257       {
2258         const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
2259         int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
2260         for ( int iN = 0; iN < nbN; iN += iQ )
2261           helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
2262       }
2263       helper.SetIsQuadratic( true );
2264     }
2265     else
2266     {
2267       iQ = 1;
2268       helper.SetIsQuadratic( false );
2269     }
2270     vector<const SMDS_MeshNode*> nodes( volTool.GetNodes(),
2271                                         volTool.GetNodes() + elem->NbNodes() );
2272     helper.SetElementsOnShape( true );
2273     if ( splitMethod._baryNode )
2274     {
2275       // make a node at barycenter
2276       volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
2277       SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
2278       nodes.push_back( gcNode );
2279       newNodes.push_back( gcNode );
2280     }
2281     if ( !splitMethod._faceBaryNode.empty() )
2282     {
2283       // make or find baricentric nodes of faces
2284       map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
2285       for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
2286       {
2287         map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
2288           volFace2BaryNode.insert
2289           ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
2290         if ( !f_n->second )
2291         {
2292           volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
2293           newNodes.push_back( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
2294         }
2295         nodes.push_back( iF_n->second = f_n->second );
2296       }
2297     }
2298
2299     // make new volumes
2300     splitVols.resize( splitMethod._nbSplits ); // splits of a volume
2301     const int* volConn = splitMethod._connectivity;
2302     if ( splitMethod._nbCorners == 4 ) // tetra
2303       for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2304         newElems.push_back( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2305                                                                nodes[ volConn[1] ],
2306                                                                nodes[ volConn[2] ],
2307                                                                nodes[ volConn[3] ]));
2308     else // prisms
2309       for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2310         newElems.push_back( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2311                                                                nodes[ volConn[1] ],
2312                                                                nodes[ volConn[2] ],
2313                                                                nodes[ volConn[3] ],
2314                                                                nodes[ volConn[4] ],
2315                                                                nodes[ volConn[5] ]));
2316
2317     ReplaceElemInGroups( elem, splitVols, GetMeshDS() );
2318
2319     // Split faces on sides of the split volume
2320
2321     const SMDS_MeshNode** volNodes = volTool.GetNodes();
2322     for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2323     {
2324       const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
2325       if ( nbNodes < 4 ) continue;
2326
2327       // find an existing face
2328       vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
2329                                            volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
2330       while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
2331                                                                        /*noMedium=*/false))
2332       {
2333         // make triangles
2334         helper.SetElementsOnShape( false );
2335         vector< const SMDS_MeshElement* > triangles;
2336
2337         // find submesh to add new triangles in
2338         if ( !fSubMesh || !fSubMesh->Contains( face ))
2339         {
2340           int shapeID = FindShape( face );
2341           fSubMesh = GetMeshDS()->MeshElements( shapeID );
2342         }
2343         map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
2344         if ( iF_n != splitMethod._faceBaryNode.end() )
2345         {
2346           const SMDS_MeshNode *baryNode = iF_n->second;
2347           for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
2348           {
2349             const SMDS_MeshNode* n1 = fNodes[iN];
2350             const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
2351             const SMDS_MeshNode *n3 = baryNode;
2352             if ( !volTool.IsFaceExternal( iF ))
2353               swap( n2, n3 );
2354             triangles.push_back( helper.AddFace( n1,n2,n3 ));
2355           }
2356           if ( fSubMesh ) // update position of the bary node on geometry
2357           {
2358             if ( subMesh )
2359               subMesh->RemoveNode( baryNode );
2360             GetMeshDS()->SetNodeOnFace( baryNode, fSubMesh->GetID() );
2361             const TopoDS_Shape& s = GetMeshDS()->IndexToShape( fSubMesh->GetID() );
2362             if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE )
2363             {
2364               fHelper.SetSubShape( s );
2365               gp_XY uv( 1e100, 1e100 );
2366               double distXYZ[4];
2367               if ( !fHelper.CheckNodeUV( TopoDS::Face( s ), baryNode,
2368                                          uv, /*tol=*/1e-7, /*force=*/true, distXYZ ) &&
2369                    uv.X() < 1e100 )
2370               {
2371                 // node is too far from the surface
2372                 GetMeshDS()->MoveNode( baryNode, distXYZ[1], distXYZ[2], distXYZ[3] );
2373                 const_cast<SMDS_MeshNode*>( baryNode )->SetPosition
2374                   ( SMDS_PositionPtr( new SMDS_FacePosition( uv.X(), uv.Y() )));
2375               }
2376             }
2377           }
2378         }
2379         else
2380         {
2381           // among possible triangles create ones described by split method
2382           const int* nInd = volTool.GetFaceNodesIndices( iF );
2383           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2384           int iCom = 0; // common node of triangle faces to split into
2385           list< TTriangleFacet > facets;
2386           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
2387           {
2388             TTriangleFacet t012( nInd[ iQ * ( iCom                )],
2389                                  nInd[ iQ * ( (iCom+1)%nbNodes )],
2390                                  nInd[ iQ * ( (iCom+2)%nbNodes )]);
2391             TTriangleFacet t023( nInd[ iQ * ( iCom                )],
2392                                  nInd[ iQ * ( (iCom+2)%nbNodes )],
2393                                  nInd[ iQ * ( (iCom+3)%nbNodes )]);
2394             if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
2395             {
2396               facets.push_back( t012 );
2397               facets.push_back( t023 );
2398               for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
2399                 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom             )],
2400                                                   nInd[ iQ * ((iLast-1)%nbNodes )],
2401                                                   nInd[ iQ * ((iLast  )%nbNodes )]));
2402               break;
2403             }
2404           }
2405           list< TTriangleFacet >::iterator facet = facets.begin();
2406           if ( facet == facets.end() )
2407             break;
2408           for ( ; facet != facets.end(); ++facet )
2409           {
2410             if ( !volTool.IsFaceExternal( iF ))
2411               swap( facet->_n2, facet->_n3 );
2412             triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
2413                                                  volNodes[ facet->_n2 ],
2414                                                  volNodes[ facet->_n3 ]));
2415           }
2416         }
2417         for ( size_t i = 0; i < triangles.size(); ++i )
2418         {
2419           if ( !triangles[ i ]) continue;
2420           if ( fSubMesh )
2421             fSubMesh->AddElement( triangles[ i ]);
2422           newElems.push_back( triangles[ i ]);
2423         }
2424         ReplaceElemInGroups( face, triangles, GetMeshDS() );
2425         GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2426
2427       } // while a face based on facet nodes exists
2428     } // loop on volume faces to split them into triangles
2429
2430     GetMeshDS()->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2431
2432     if ( geomType == SMDSEntity_TriQuad_Hexa )
2433     {
2434       // remove medium nodes that could become free
2435       for ( int i = 20; i < volTool.NbNodes(); ++i )
2436         if ( volNodes[i]->NbInverseElements() == 0 )
2437           GetMeshDS()->RemoveNode( volNodes[i] );
2438     }
2439   } // loop on volumes to split
2440
2441   myLastCreatedNodes = newNodes;
2442   myLastCreatedElems = newElems;
2443 }
2444
2445 //=======================================================================
2446 //function : GetHexaFacetsToSplit
2447 //purpose  : For hexahedra that will be split into prisms, finds facets to
2448 //           split into triangles. Only hexahedra adjacent to the one closest
2449 //           to theFacetNormal.Location() are returned.
2450 //param [in,out] theHexas - the hexahedra
2451 //param [in]     theFacetNormal - facet normal
2452 //param [out]    theFacets - the hexahedra and found facet IDs
2453 //=======================================================================
2454
2455 void SMESH_MeshEditor::GetHexaFacetsToSplit( TIDSortedElemSet& theHexas,
2456                                              const gp_Ax1&     theFacetNormal,
2457                                              TFacetOfElem &    theFacets)
2458 {
2459 #define THIS_METHOD "SMESH_MeshEditor::GetHexaFacetsToSplit(): "
2460
2461   // Find a hexa closest to the location of theFacetNormal
2462
2463   const SMDS_MeshElement* startHex;
2464   {
2465     // get SMDS_ElemIteratorPtr on theHexas
2466     typedef const SMDS_MeshElement*                                      TValue;
2467     typedef TIDSortedElemSet::iterator                                   TSetIterator;
2468     typedef SMDS::SimpleAccessor<TValue,TSetIterator>                    TAccesor;
2469     typedef SMDS_MeshElement::GeomFilter                                 TFilter;
2470     typedef SMDS_SetIterator < TValue, TSetIterator, TAccesor, TFilter > TElemSetIter;
2471     SMDS_ElemIteratorPtr elemIt = SMDS_ElemIteratorPtr
2472       ( new TElemSetIter( theHexas.begin(),
2473                           theHexas.end(),
2474                           SMDS_MeshElement::GeomFilter( SMDSGeom_HEXA )));
2475
2476     SMESH_ElementSearcher* searcher =
2477       SMESH_MeshAlgos::GetElementSearcher( *myMesh->GetMeshDS(), elemIt );
2478
2479     startHex = searcher->FindClosestTo( theFacetNormal.Location(), SMDSAbs_Volume );
2480
2481     delete searcher;
2482
2483     if ( !startHex )
2484       throw SALOME_Exception( THIS_METHOD "startHex not found");
2485   }
2486
2487   // Select a facet of startHex by theFacetNormal
2488
2489   SMDS_VolumeTool vTool( startHex );
2490   double norm[3], dot, maxDot = 0;
2491   int facetID = -1;
2492   for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2493     if ( vTool.GetFaceNormal( iF, norm[0], norm[1], norm[2] ))
2494     {
2495       dot = Abs( theFacetNormal.Direction().Dot( gp_Dir( norm[0], norm[1], norm[2] )));
2496       if ( dot > maxDot )
2497       {
2498         facetID = iF;
2499         maxDot = dot;
2500       }
2501     }
2502   if ( facetID < 0 )
2503     throw SALOME_Exception( THIS_METHOD "facet of startHex not found");
2504
2505   // Fill theFacets starting from facetID of startHex
2506
2507   // facets used for searching of volumes adjacent to already treated ones
2508   typedef pair< TFacetOfElem::iterator, int > TElemFacets;
2509   typedef map< TVolumeFaceKey, TElemFacets  > TFacetMap;
2510   TFacetMap facetsToCheck;
2511
2512   set<const SMDS_MeshNode*> facetNodes;
2513   const SMDS_MeshElement*   curHex;
2514
2515   const bool allHex = ((int) theHexas.size() == myMesh->NbHexas() );
2516
2517   while ( startHex )
2518   {
2519     // move in two directions from startHex via facetID
2520     for ( int is2nd = 0; is2nd < 2; ++is2nd )
2521     {
2522       curHex       = startHex;
2523       int curFacet = facetID;
2524       if ( is2nd ) // do not treat startHex twice
2525       {
2526         vTool.Set( curHex );
2527         if ( vTool.IsFreeFace( curFacet, &curHex ))
2528         {
2529           curHex = 0;
2530         }
2531         else
2532         {
2533           vTool.GetFaceNodes( curFacet, facetNodes );
2534           vTool.Set( curHex );
2535           curFacet = vTool.GetFaceIndex( facetNodes );
2536         }
2537       }
2538       while ( curHex )
2539       {
2540         // store a facet to split
2541         if ( curHex->GetGeomType() != SMDSGeom_HEXA )
2542         {
2543           theFacets.insert( make_pair( curHex, -1 ));
2544           break;
2545         }
2546         if ( !allHex && !theHexas.count( curHex ))
2547           break;
2548
2549         pair< TFacetOfElem::iterator, bool > facetIt2isNew =
2550           theFacets.insert( make_pair( curHex, curFacet ));
2551         if ( !facetIt2isNew.second )
2552           break;
2553
2554         // remember not-to-split facets in facetsToCheck
2555         int oppFacet = vTool.GetOppFaceIndexOfHex( curFacet );
2556         for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2557         {
2558           if ( iF == curFacet && iF == oppFacet )
2559             continue;
2560           TVolumeFaceKey facetKey ( vTool, iF );
2561           TElemFacets    elemFacet( facetIt2isNew.first, iF );
2562           pair< TFacetMap::iterator, bool > it2isnew =
2563             facetsToCheck.insert( make_pair( facetKey, elemFacet ));
2564           if ( !it2isnew.second )
2565             facetsToCheck.erase( it2isnew.first ); // adjacent hex already checked
2566         }
2567         // pass to a volume adjacent via oppFacet
2568         if ( vTool.IsFreeFace( oppFacet, &curHex ))
2569         {
2570           curHex = 0;
2571         }
2572         else
2573         {
2574           // get a new curFacet
2575           vTool.GetFaceNodes( oppFacet, facetNodes );
2576           vTool.Set( curHex );
2577           curFacet = vTool.GetFaceIndex( facetNodes, /*hint=*/curFacet );
2578         }
2579       }
2580     } // move in two directions from startHex via facetID
2581
2582     // Find a new startHex by facetsToCheck
2583
2584     startHex = 0;
2585     facetID  = -1;
2586     TFacetMap::iterator fIt = facetsToCheck.begin();
2587     while ( !startHex && fIt != facetsToCheck.end() )
2588     {
2589       const TElemFacets&  elemFacets = fIt->second;
2590       const SMDS_MeshElement*    hex = elemFacets.first->first;
2591       int                 splitFacet = elemFacets.first->second;
2592       int               lateralFacet = elemFacets.second;
2593       facetsToCheck.erase( fIt );
2594       fIt = facetsToCheck.begin();
2595
2596       vTool.Set( hex );
2597       if ( vTool.IsFreeFace( lateralFacet, &curHex ) || 
2598            curHex->GetGeomType() != SMDSGeom_HEXA )
2599         continue;
2600       if ( !allHex && !theHexas.count( curHex ))
2601         continue;
2602
2603       startHex = curHex;
2604
2605       // find a facet of startHex to split
2606
2607       set<const SMDS_MeshNode*> lateralNodes;
2608       vTool.GetFaceNodes( lateralFacet, lateralNodes );
2609       vTool.GetFaceNodes( splitFacet,   facetNodes );
2610       int oppLateralFacet = vTool.GetOppFaceIndexOfHex( lateralFacet );
2611       vTool.Set( startHex );
2612       lateralFacet = vTool.GetFaceIndex( lateralNodes, oppLateralFacet );
2613
2614       // look for a facet of startHex having common nodes with facetNodes
2615       // but not lateralFacet
2616       for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2617       {
2618         if ( iF == lateralFacet )
2619           continue;
2620         int nbCommonNodes = 0;
2621         const SMDS_MeshNode** nn = vTool.GetFaceNodes( iF );
2622         for ( int iN = 0, nbN = vTool.NbFaceNodes( iF ); iN < nbN; ++iN )
2623           nbCommonNodes += facetNodes.count( nn[ iN ]);
2624
2625         if ( nbCommonNodes >= 2 )
2626         {
2627           facetID = iF;
2628           break;
2629         }
2630       }
2631       if ( facetID < 0 )
2632         throw SALOME_Exception( THIS_METHOD "facet of a new startHex not found");
2633     }
2634   } //   while ( startHex )
2635
2636   return;
2637 }
2638
2639 namespace
2640 {
2641   //================================================================================
2642   /*!
2643    * \brief Selects nodes of several elements according to a given interlace
2644    *  \param [in] srcNodes - nodes to select from
2645    *  \param [out] tgtNodesVec - array of nodes of several elements to fill in
2646    *  \param [in] interlace - indices of nodes for all elements
2647    *  \param [in] nbElems - nb of elements
2648    *  \param [in] nbNodes - nb of nodes in each element
2649    *  \param [in] mesh - the mesh
2650    *  \param [out] elemQueue - a list to push elements found by the selected nodes
2651    *  \param [in] type - type of elements to look for
2652    */
2653   //================================================================================
2654
2655   void selectNodes( const vector< const SMDS_MeshNode* >& srcNodes,
2656                     vector< const SMDS_MeshNode* >*       tgtNodesVec,
2657                     const int*                            interlace,
2658                     const int                             nbElems,
2659                     const int                             nbNodes,
2660                     SMESHDS_Mesh*                         mesh = 0,
2661                     list< const SMDS_MeshElement* >*      elemQueue=0,
2662                     SMDSAbs_ElementType                   type=SMDSAbs_All)
2663   {
2664     for ( int iE = 0; iE < nbElems; ++iE )
2665     {
2666       vector< const SMDS_MeshNode* >& elemNodes = tgtNodesVec[iE];
2667       const int*                         select = & interlace[iE*nbNodes];
2668       elemNodes.resize( nbNodes );
2669       for ( int iN = 0; iN < nbNodes; ++iN )
2670         elemNodes[iN] = srcNodes[ select[ iN ]];
2671     }
2672     const SMDS_MeshElement* e;
2673     if ( elemQueue )
2674       for ( int iE = 0; iE < nbElems; ++iE )
2675         if (( e = mesh->FindElement( tgtNodesVec[iE], type, /*noMedium=*/false)))
2676           elemQueue->push_back( e );
2677   }
2678 }
2679
2680 //=======================================================================
2681 /*
2682  * Split bi-quadratic elements into linear ones without creation of additional nodes
2683  *   - bi-quadratic triangle will be split into 3 linear quadrangles;
2684  *   - bi-quadratic quadrangle will be split into 4 linear quadrangles;
2685  *   - tri-quadratic hexahedron will be split into 8 linear hexahedra;
2686  *   Quadratic elements of lower dimension  adjacent to the split bi-quadratic element
2687  *   will be split in order to keep the mesh conformal.
2688  *  \param elems - elements to split
2689  */
2690 //=======================================================================
2691
2692 void SMESH_MeshEditor::SplitBiQuadraticIntoLinear(TIDSortedElemSet& theElems)
2693 {
2694   vector< const SMDS_MeshNode* > elemNodes(27), subNodes[12], splitNodes[8];
2695   vector<const SMDS_MeshElement* > splitElems;
2696   list< const SMDS_MeshElement* > elemQueue;
2697   list< const SMDS_MeshElement* >::iterator elemIt;
2698
2699   SMESHDS_Mesh * mesh = GetMeshDS();
2700   ElemFeatures *elemType, hexaType(SMDSAbs_Volume), quadType(SMDSAbs_Face), segType(SMDSAbs_Edge);
2701   int nbElems, nbNodes;
2702
2703   TIDSortedElemSet::iterator elemSetIt = theElems.begin();
2704   for ( ; elemSetIt != theElems.end(); ++elemSetIt )
2705   {
2706     elemQueue.clear();
2707     elemQueue.push_back( *elemSetIt );
2708     for ( elemIt = elemQueue.begin(); elemIt != elemQueue.end(); ++elemIt )
2709     {
2710       const SMDS_MeshElement* elem = *elemIt;
2711       switch( elem->GetEntityType() )
2712       {
2713       case SMDSEntity_TriQuad_Hexa: // HEX27
2714       {
2715         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2716         nbElems  = nbNodes = 8;
2717         elemType = & hexaType;
2718
2719         // get nodes for new elements
2720         static int vInd[8][8] = {{ 0,8,20,11,   16,21,26,24 },
2721                                  { 1,9,20,8,    17,22,26,21 },
2722                                  { 2,10,20,9,   18,23,26,22 },
2723                                  { 3,11,20,10,  19,24,26,23 },
2724                                  { 16,21,26,24, 4,12,25,15  },
2725                                  { 17,22,26,21, 5,13,25,12  },
2726                                  { 18,23,26,22, 6,14,25,13  },
2727                                  { 19,24,26,23, 7,15,25,14  }};
2728         selectNodes( elemNodes, & splitNodes[0], &vInd[0][0], nbElems, nbNodes );
2729
2730         // add boundary faces to elemQueue
2731         static int fInd[6][9] = {{ 0,1,2,3, 8,9,10,11,   20 },
2732                                  { 4,5,6,7, 12,13,14,15, 25 },
2733                                  { 0,1,5,4, 8,17,12,16,  21 },
2734                                  { 1,2,6,5, 9,18,13,17,  22 },
2735                                  { 2,3,7,6, 10,19,14,18, 23 },
2736                                  { 3,0,4,7, 11,16,15,19, 24 }};
2737         selectNodes( elemNodes, & subNodes[0], &fInd[0][0], 6,9, mesh, &elemQueue, SMDSAbs_Face );
2738
2739         // add boundary segments to elemQueue
2740         static int eInd[12][3] = {{ 0,1,8 }, { 1,2,9 }, { 2,3,10 }, { 3,0,11 },
2741                                   { 4,5,12}, { 5,6,13}, { 6,7,14 }, { 7,4,15 },
2742                                   { 0,4,16}, { 1,5,17}, { 2,6,18 }, { 3,7,19 }};
2743         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 12,3, mesh, &elemQueue, SMDSAbs_Edge );
2744         break;
2745       }
2746       case SMDSEntity_BiQuad_Triangle: // TRIA7
2747       {
2748         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2749         nbElems = 3;
2750         nbNodes = 4;
2751         elemType = & quadType;
2752
2753         // get nodes for new elements
2754         static int fInd[3][4] = {{ 0,3,6,5 }, { 1,4,6,3 }, { 2,5,6,4 }};
2755         selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2756
2757         // add boundary segments to elemQueue
2758         static int eInd[3][3] = {{ 0,1,3 }, { 1,2,4 }, { 2,0,5 }};
2759         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 3,3, mesh, &elemQueue, SMDSAbs_Edge );
2760         break;
2761       }
2762       case SMDSEntity_BiQuad_Quadrangle: // QUAD9
2763       {
2764         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2765         nbElems = 4;
2766         nbNodes = 4;
2767         elemType = & quadType;
2768
2769         // get nodes for new elements
2770         static int fInd[4][4] = {{ 0,4,8,7 }, { 1,5,8,4 }, { 2,6,8,5 }, { 3,7,8,6 }};
2771         selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2772
2773         // add boundary segments to elemQueue
2774         static int eInd[4][3] = {{ 0,1,4 }, { 1,2,5 }, { 2,3,6 }, { 3,0,7 }};
2775         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 4,3, mesh, &elemQueue, SMDSAbs_Edge );
2776         break;
2777       }
2778       case SMDSEntity_Quad_Edge:
2779       {
2780         if ( elemIt == elemQueue.begin() )
2781           continue; // an elem is in theElems
2782         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2783         nbElems = 2;
2784         nbNodes = 2;
2785         elemType = & segType;
2786
2787         // get nodes for new elements
2788         static int eInd[2][2] = {{ 0,2 }, { 2,1 }};
2789         selectNodes( elemNodes, & splitNodes[0], &eInd[0][0], nbElems, nbNodes );
2790         break;
2791       }
2792       default: continue;
2793       } // switch( elem->GetEntityType() )
2794
2795       // Create new elements
2796
2797       SMESHDS_SubMesh* subMesh = mesh->MeshElements( elem->getshapeId() );
2798
2799       splitElems.clear();
2800
2801       //elemType->SetID( elem->GetID() ); // create an elem with the same ID as a removed one
2802       mesh->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2803       //splitElems.push_back( AddElement( splitNodes[ 0 ], *elemType ));
2804       //elemType->SetID( -1 );
2805
2806       for ( int iE = 0; iE < nbElems; ++iE )
2807         splitElems.push_back( AddElement( splitNodes[ iE ], *elemType ));
2808
2809
2810       ReplaceElemInGroups( elem, splitElems, mesh );
2811
2812       if ( subMesh )
2813         for ( size_t i = 0; i < splitElems.size(); ++i )
2814           subMesh->AddElement( splitElems[i] );
2815     }
2816   }
2817 }
2818
2819 //=======================================================================
2820 //function : AddToSameGroups
2821 //purpose  : add elemToAdd to the groups the elemInGroups belongs to
2822 //=======================================================================
2823
2824 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
2825                                         const SMDS_MeshElement* elemInGroups,
2826                                         SMESHDS_Mesh *          aMesh)
2827 {
2828   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2829   if (!groups.empty()) {
2830     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2831     for ( ; grIt != groups.end(); grIt++ ) {
2832       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2833       if ( group && group->Contains( elemInGroups ))
2834         group->SMDSGroup().Add( elemToAdd );
2835     }
2836   }
2837 }
2838
2839
2840 //=======================================================================
2841 //function : RemoveElemFromGroups
2842 //purpose  : Remove removeelem to the groups the elemInGroups belongs to
2843 //=======================================================================
2844 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2845                                              SMESHDS_Mesh *          aMesh)
2846 {
2847   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2848   if (!groups.empty())
2849   {
2850     set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2851     for (; GrIt != groups.end(); GrIt++)
2852     {
2853       SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2854       if (!grp || grp->IsEmpty()) continue;
2855       grp->SMDSGroup().Remove(removeelem);
2856     }
2857   }
2858 }
2859
2860 //================================================================================
2861 /*!
2862  * \brief Replace elemToRm by elemToAdd in the all groups
2863  */
2864 //================================================================================
2865
2866 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2867                                             const SMDS_MeshElement* elemToAdd,
2868                                             SMESHDS_Mesh *          aMesh)
2869 {
2870   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2871   if (!groups.empty()) {
2872     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2873     for ( ; grIt != groups.end(); grIt++ ) {
2874       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2875       if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2876         group->SMDSGroup().Add( elemToAdd );
2877     }
2878   }
2879 }
2880
2881 //================================================================================
2882 /*!
2883  * \brief Replace elemToRm by elemToAdd in the all groups
2884  */
2885 //================================================================================
2886
2887 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement*                elemToRm,
2888                                             const vector<const SMDS_MeshElement*>& elemToAdd,
2889                                             SMESHDS_Mesh *                         aMesh)
2890 {
2891   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2892   if (!groups.empty())
2893   {
2894     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2895     for ( ; grIt != groups.end(); grIt++ ) {
2896       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2897       if ( group && group->SMDSGroup().Remove( elemToRm ) )
2898         for ( size_t i = 0; i < elemToAdd.size(); ++i )
2899           group->SMDSGroup().Add( elemToAdd[ i ] );
2900     }
2901   }
2902 }
2903
2904 //=======================================================================
2905 //function : QuadToTri
2906 //purpose  : Cut quadrangles into triangles.
2907 //           theCrit is used to select a diagonal to cut
2908 //=======================================================================
2909
2910 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2911                                   const bool         the13Diag)
2912 {
2913   ClearLastCreated();
2914   myLastCreatedElems.reserve( theElems.size() * 2 );
2915
2916   SMESHDS_Mesh *       aMesh = GetMeshDS();
2917   Handle(Geom_Surface) surface;
2918   SMESH_MesherHelper   helper( *GetMesh() );
2919
2920   TIDSortedElemSet::iterator itElem;
2921   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
2922   {
2923     const SMDS_MeshElement* elem = *itElem;
2924     if ( !elem || elem->GetGeomType() != SMDSGeom_QUADRANGLE )
2925       continue;
2926
2927     if ( elem->NbNodes() == 4 ) {
2928       // retrieve element nodes
2929       const SMDS_MeshNode* aNodes [4];
2930       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2931       int i = 0;
2932       while ( itN->more() )
2933         aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2934
2935       int aShapeId = FindShape( elem );
2936       const SMDS_MeshElement* newElem1 = 0;
2937       const SMDS_MeshElement* newElem2 = 0;
2938       if ( the13Diag ) {
2939         newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2940         newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2941       }
2942       else {
2943         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2944         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2945       }
2946       myLastCreatedElems.push_back(newElem1);
2947       myLastCreatedElems.push_back(newElem2);
2948       // put a new triangle on the same shape and add to the same groups
2949       if ( aShapeId )
2950       {
2951         aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2952         aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2953       }
2954       AddToSameGroups( newElem1, elem, aMesh );
2955       AddToSameGroups( newElem2, elem, aMesh );
2956       aMesh->RemoveElement( elem );
2957     }
2958
2959     // Quadratic quadrangle
2960
2961     else if ( elem->NbNodes() >= 8 )
2962     {
2963       // get surface elem is on
2964       int aShapeId = FindShape( elem );
2965       if ( aShapeId != helper.GetSubShapeID() ) {
2966         surface.Nullify();
2967         TopoDS_Shape shape;
2968         if ( aShapeId > 0 )
2969           shape = aMesh->IndexToShape( aShapeId );
2970         if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
2971           TopoDS_Face face = TopoDS::Face( shape );
2972           surface = BRep_Tool::Surface( face );
2973           if ( !surface.IsNull() )
2974             helper.SetSubShape( shape );
2975         }
2976       }
2977
2978       const SMDS_MeshNode* aNodes [9]; aNodes[8] = 0;
2979       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2980       for ( int i = 0; itN->more(); ++i )
2981         aNodes[ i ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2982
2983       const SMDS_MeshNode* centrNode = aNodes[8];
2984       if ( centrNode == 0 )
2985       {
2986         centrNode = helper.GetCentralNode( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
2987                                            aNodes[4], aNodes[5], aNodes[6], aNodes[7],
2988                                            surface.IsNull() );
2989         myLastCreatedNodes.push_back(centrNode);
2990       }
2991
2992       // create a new element
2993       const SMDS_MeshElement* newElem1 = 0;
2994       const SMDS_MeshElement* newElem2 = 0;
2995       if ( the13Diag ) {
2996         newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
2997                                   aNodes[6], aNodes[7], centrNode );
2998         newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
2999                                   centrNode, aNodes[4], aNodes[5] );
3000       }
3001       else {
3002         newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
3003                                   aNodes[7], aNodes[4], centrNode );
3004         newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
3005                                   centrNode, aNodes[5], aNodes[6] );
3006       }
3007       myLastCreatedElems.push_back(newElem1);
3008       myLastCreatedElems.push_back(newElem2);
3009       // put a new triangle on the same shape and add to the same groups
3010       if ( aShapeId )
3011       {
3012         aMesh->SetMeshElementOnShape( newElem1, aShapeId );
3013         aMesh->SetMeshElementOnShape( newElem2, aShapeId );
3014       }
3015       AddToSameGroups( newElem1, elem, aMesh );
3016       AddToSameGroups( newElem2, elem, aMesh );
3017       aMesh->RemoveElement( elem );
3018     }
3019   }
3020
3021   return true;
3022 }
3023
3024 //=======================================================================
3025 //function : getAngle
3026 //purpose  :
3027 //=======================================================================
3028
3029 double getAngle(const SMDS_MeshElement * tr1,
3030                 const SMDS_MeshElement * tr2,
3031                 const SMDS_MeshNode *    n1,
3032                 const SMDS_MeshNode *    n2)
3033 {
3034   double angle = 2. * M_PI; // bad angle
3035
3036   // get normals
3037   SMESH::Controls::TSequenceOfXYZ P1, P2;
3038   if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
3039        !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
3040     return angle;
3041   gp_Vec N1,N2;
3042   if(!tr1->IsQuadratic())
3043     N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
3044   else
3045     N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
3046   if ( N1.SquareMagnitude() <= gp::Resolution() )
3047     return angle;
3048   if(!tr2->IsQuadratic())
3049     N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
3050   else
3051     N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
3052   if ( N2.SquareMagnitude() <= gp::Resolution() )
3053     return angle;
3054
3055   // find the first diagonal node n1 in the triangles:
3056   // take in account a diagonal link orientation
3057   const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
3058   for ( int t = 0; t < 2; t++ ) {
3059     SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
3060     int i = 0, iDiag = -1;
3061     while ( it->more()) {
3062       const SMDS_MeshElement *n = it->next();
3063       if ( n == n1 || n == n2 ) {
3064         if ( iDiag < 0)
3065           iDiag = i;
3066         else {
3067           if ( i - iDiag == 1 )
3068             nFirst[ t ] = ( n == n1 ? n2 : n1 );
3069           else
3070             nFirst[ t ] = n;
3071           break;
3072         }
3073       }
3074       i++;
3075     }
3076   }
3077   if ( nFirst[ 0 ] == nFirst[ 1 ] )
3078     N2.Reverse();
3079
3080   angle = N1.Angle( N2 );
3081   //SCRUTE( angle );
3082   return angle;
3083 }
3084
3085 // =================================================
3086 // class generating a unique ID for a pair of nodes
3087 // and able to return nodes by that ID
3088 // =================================================
3089 class LinkID_Gen {
3090 public:
3091
3092   LinkID_Gen( const SMESHDS_Mesh* theMesh )
3093     :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
3094   {}
3095
3096   long GetLinkID (const SMDS_MeshNode * n1,
3097                   const SMDS_MeshNode * n2) const
3098   {
3099     return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
3100   }
3101
3102   bool GetNodes (const long             theLinkID,
3103                  const SMDS_MeshNode* & theNode1,
3104                  const SMDS_MeshNode* & theNode2) const
3105   {
3106     theNode1 = myMesh->FindNode( theLinkID / myMaxID );
3107     if ( !theNode1 ) return false;
3108     theNode2 = myMesh->FindNode( theLinkID % myMaxID );
3109     if ( !theNode2 ) return false;
3110     return true;
3111   }
3112
3113 private:
3114   LinkID_Gen();
3115   const SMESHDS_Mesh* myMesh;
3116   long                myMaxID;
3117 };
3118
3119
3120 //=======================================================================
3121 //function : TriToQuad
3122 //purpose  : Fuse neighbour triangles into quadrangles.
3123 //           theCrit is used to select a neighbour to fuse with.
3124 //           theMaxAngle is a max angle between element normals at which
3125 //           fusion is still performed.
3126 //=======================================================================
3127
3128 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet &                   theElems,
3129                                   SMESH::Controls::NumericalFunctorPtr theCrit,
3130                                   const double                         theMaxAngle)
3131 {
3132   ClearLastCreated();
3133   myLastCreatedElems.reserve( theElems.size() / 2 );
3134
3135   if ( !theCrit.get() )
3136     return false;
3137
3138   SMESHDS_Mesh * aMesh = GetMeshDS();
3139
3140   // Prepare data for algo: build
3141   // 1. map of elements with their linkIDs
3142   // 2. map of linkIDs with their elements
3143
3144   map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
3145   map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
3146   map< const SMDS_MeshElement*, set< SMESH_TLink > >  mapEl_setLi;
3147   map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
3148
3149   TIDSortedElemSet::iterator itElem;
3150   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
3151   {
3152     const SMDS_MeshElement* elem = *itElem;
3153     if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
3154     bool IsTria = ( elem->NbCornerNodes()==3 );
3155     if (!IsTria) continue;
3156
3157     // retrieve element nodes
3158     const SMDS_MeshNode* aNodes [4];
3159     SMDS_NodeIteratorPtr itN = elem->nodeIterator();
3160     int i = 0;
3161     while ( i < 3 )
3162       aNodes[ i++ ] = itN->next();
3163     aNodes[ 3 ] = aNodes[ 0 ];
3164
3165     // fill maps
3166     for ( i = 0; i < 3; i++ ) {
3167       SMESH_TLink link( aNodes[i], aNodes[i+1] );
3168       // check if elements sharing a link can be fused
3169       itLE = mapLi_listEl.find( link );
3170       if ( itLE != mapLi_listEl.end() ) {
3171         if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
3172           continue;
3173         const SMDS_MeshElement* elem2 = (*itLE).second.front();
3174         //if ( FindShape( elem ) != FindShape( elem2 ))
3175         //  continue; // do not fuse triangles laying on different shapes
3176         if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
3177           continue; // avoid making badly shaped quads
3178         (*itLE).second.push_back( elem );
3179       }
3180       else {
3181         mapLi_listEl[ link ].push_back( elem );
3182       }
3183       mapEl_setLi [ elem ].insert( link );
3184     }
3185   }
3186   // Clean the maps from the links shared by a sole element, ie
3187   // links to which only one element is bound in mapLi_listEl
3188
3189   for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
3190     int nbElems = (*itLE).second.size();
3191     if ( nbElems < 2  ) {
3192       const SMDS_MeshElement* elem = (*itLE).second.front();
3193       SMESH_TLink link = (*itLE).first;
3194       mapEl_setLi[ elem ].erase( link );
3195       if ( mapEl_setLi[ elem ].empty() )
3196         mapEl_setLi.erase( elem );
3197     }
3198   }
3199
3200   // Algo: fuse triangles into quadrangles
3201
3202   while ( ! mapEl_setLi.empty() ) {
3203     // Look for the start element:
3204     // the element having the least nb of shared links
3205     const SMDS_MeshElement* startElem = 0;
3206     int minNbLinks = 4;
3207     for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
3208       int nbLinks = (*itEL).second.size();
3209       if ( nbLinks < minNbLinks ) {
3210         startElem = (*itEL).first;
3211         minNbLinks = nbLinks;
3212         if ( minNbLinks == 1 )
3213           break;
3214       }
3215     }
3216
3217     // search elements to fuse starting from startElem or links of elements
3218     // fused earlyer - startLinks
3219     list< SMESH_TLink > startLinks;
3220     while ( startElem || !startLinks.empty() ) {
3221       while ( !startElem && !startLinks.empty() ) {
3222         // Get an element to start, by a link
3223         SMESH_TLink linkId = startLinks.front();
3224         startLinks.pop_front();
3225         itLE = mapLi_listEl.find( linkId );
3226         if ( itLE != mapLi_listEl.end() ) {
3227           list< const SMDS_MeshElement* > & listElem = (*itLE).second;
3228           list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
3229           for ( ; itE != listElem.end() ; itE++ )
3230             if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
3231               startElem = (*itE);
3232           mapLi_listEl.erase( itLE );
3233         }
3234       }
3235
3236       if ( startElem ) {
3237         // Get candidates to be fused
3238         const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
3239         const SMESH_TLink *link12 = 0, *link13 = 0;
3240         startElem = 0;
3241         ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
3242         set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
3243         ASSERT( !setLi.empty() );
3244         set< SMESH_TLink >::iterator itLi;
3245         for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
3246         {
3247           const SMESH_TLink & link = (*itLi);
3248           itLE = mapLi_listEl.find( link );
3249           if ( itLE == mapLi_listEl.end() )
3250             continue;
3251
3252           const SMDS_MeshElement* elem = (*itLE).second.front();
3253           if ( elem == tr1 )
3254             elem = (*itLE).second.back();
3255           mapLi_listEl.erase( itLE );
3256           if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
3257             continue;
3258           if ( tr2 ) {
3259             tr3 = elem;
3260             link13 = &link;
3261           }
3262           else {
3263             tr2 = elem;
3264             link12 = &link;
3265           }
3266
3267           // add other links of elem to list of links to re-start from
3268           set< SMESH_TLink >& links = mapEl_setLi[ elem ];
3269           set< SMESH_TLink >::iterator it;
3270           for ( it = links.begin(); it != links.end(); it++ ) {
3271             const SMESH_TLink& link2 = (*it);
3272             if ( link2 != link )
3273               startLinks.push_back( link2 );
3274           }
3275         }
3276
3277         // Get nodes of possible quadrangles
3278         const SMDS_MeshNode *n12 [4], *n13 [4];
3279         bool Ok12 = false, Ok13 = false;
3280         const SMDS_MeshNode *linkNode1, *linkNode2;
3281         if(tr2) {
3282           linkNode1 = link12->first;
3283           linkNode2 = link12->second;
3284           if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
3285             Ok12 = true;
3286         }
3287         if(tr3) {
3288           linkNode1 = link13->first;
3289           linkNode2 = link13->second;
3290           if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
3291             Ok13 = true;
3292         }
3293
3294         // Choose a pair to fuse
3295         if ( Ok12 && Ok13 ) {
3296           SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
3297           SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
3298           double aBadRate12 = getBadRate( &quad12, theCrit );
3299           double aBadRate13 = getBadRate( &quad13, theCrit );
3300           if (  aBadRate13 < aBadRate12 )
3301             Ok12 = false;
3302           else
3303             Ok13 = false;
3304         }
3305
3306         // Make quadrangles
3307         // and remove fused elems and remove links from the maps
3308         mapEl_setLi.erase( tr1 );
3309         if ( Ok12 )
3310         {
3311           mapEl_setLi.erase( tr2 );
3312           mapLi_listEl.erase( *link12 );
3313           if ( tr1->NbNodes() == 3 )
3314           {
3315             const SMDS_MeshElement* newElem = 0;
3316             newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
3317             myLastCreatedElems.push_back(newElem);
3318             AddToSameGroups( newElem, tr1, aMesh );
3319             int aShapeId = tr1->getshapeId();
3320             if ( aShapeId )
3321               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3322             aMesh->RemoveElement( tr1 );
3323             aMesh->RemoveElement( tr2 );
3324           }
3325           else {
3326             vector< const SMDS_MeshNode* > N1;
3327             vector< const SMDS_MeshNode* > N2;
3328             getNodesFromTwoTria(tr1,tr2,N1,N2);
3329             // now we receive following N1 and N2 (using numeration as in image in InverseDiag())
3330             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
3331             // i.e. first nodes from both arrays form a new diagonal
3332             const SMDS_MeshNode* aNodes[8];
3333             aNodes[0] = N1[0];
3334             aNodes[1] = N1[1];
3335             aNodes[2] = N2[0];
3336             aNodes[3] = N2[1];
3337             aNodes[4] = N1[3];
3338             aNodes[5] = N2[5];
3339             aNodes[6] = N2[3];
3340             aNodes[7] = N1[5];
3341             const SMDS_MeshElement* newElem = 0;
3342             if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3343               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3344                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3345             else
3346               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3347                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3348             myLastCreatedElems.push_back(newElem);
3349             AddToSameGroups( newElem, tr1, aMesh );
3350             int aShapeId = tr1->getshapeId();
3351             if ( aShapeId )
3352               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3353             aMesh->RemoveElement( tr1 );
3354             aMesh->RemoveElement( tr2 );
3355             // remove middle node (9)
3356             if ( N1[4]->NbInverseElements() == 0 )
3357               aMesh->RemoveNode( N1[4] );
3358             if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3359               aMesh->RemoveNode( N1[6] );
3360             if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3361               aMesh->RemoveNode( N2[6] );
3362           }
3363         }
3364         else if ( Ok13 )
3365         {
3366           mapEl_setLi.erase( tr3 );
3367           mapLi_listEl.erase( *link13 );
3368           if ( tr1->NbNodes() == 3 ) {
3369             const SMDS_MeshElement* newElem = 0;
3370             newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
3371             myLastCreatedElems.push_back(newElem);
3372             AddToSameGroups( newElem, tr1, aMesh );
3373             int aShapeId = tr1->getshapeId();
3374             if ( aShapeId )
3375               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3376             aMesh->RemoveElement( tr1 );
3377             aMesh->RemoveElement( tr3 );
3378           }
3379           else {
3380             vector< const SMDS_MeshNode* > N1;
3381             vector< const SMDS_MeshNode* > N2;
3382             getNodesFromTwoTria(tr1,tr3,N1,N2);
3383             // now we receive following N1 and N2 (using numeration as above image)
3384             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
3385             // i.e. first nodes from both arrays form a new diagonal
3386             const SMDS_MeshNode* aNodes[8];
3387             aNodes[0] = N1[0];
3388             aNodes[1] = N1[1];
3389             aNodes[2] = N2[0];
3390             aNodes[3] = N2[1];
3391             aNodes[4] = N1[3];
3392             aNodes[5] = N2[5];
3393             aNodes[6] = N2[3];
3394             aNodes[7] = N1[5];
3395             const SMDS_MeshElement* newElem = 0;
3396             if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3397               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3398                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3399             else
3400               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3401                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3402             myLastCreatedElems.push_back(newElem);
3403             AddToSameGroups( newElem, tr1, aMesh );
3404             int aShapeId = tr1->getshapeId();
3405             if ( aShapeId )
3406               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3407             aMesh->RemoveElement( tr1 );
3408             aMesh->RemoveElement( tr3 );
3409             // remove middle node (9)
3410             if ( N1[4]->NbInverseElements() == 0 )
3411               aMesh->RemoveNode( N1[4] );
3412             if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3413               aMesh->RemoveNode( N1[6] );
3414             if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3415               aMesh->RemoveNode( N2[6] );
3416           }
3417         }
3418
3419         // Next element to fuse: the rejected one
3420         if ( tr3 )
3421           startElem = Ok12 ? tr3 : tr2;
3422
3423       } // if ( startElem )
3424     } // while ( startElem || !startLinks.empty() )
3425   } // while ( ! mapEl_setLi.empty() )
3426
3427   return true;
3428 }
3429
3430 //================================================================================
3431 /*!
3432  * \brief Return nodes linked to the given one
3433  * \param theNode - the node
3434  * \param linkedNodes - the found nodes
3435  * \param type - the type of elements to check
3436  *
3437  * Medium nodes are ignored
3438  */
3439 //================================================================================
3440
3441 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
3442                                        TIDSortedElemSet &   linkedNodes,
3443                                        SMDSAbs_ElementType  type )
3444 {
3445   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
3446   while ( elemIt->more() )
3447   {
3448     const SMDS_MeshElement* elem = elemIt->next();
3449     if(elem->GetType() == SMDSAbs_0DElement)
3450       continue;
3451
3452     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
3453     if ( elem->GetType() == SMDSAbs_Volume )
3454     {
3455       SMDS_VolumeTool vol( elem );
3456       while ( nodeIt->more() ) {
3457         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3458         if ( theNode != n && vol.IsLinked( theNode, n ))
3459           linkedNodes.insert( n );
3460       }
3461     }
3462     else
3463     {
3464       for ( int i = 0; nodeIt->more(); ++i ) {
3465         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3466         if ( n == theNode ) {
3467           int iBefore = i - 1;
3468           int iAfter  = i + 1;
3469           if ( elem->IsQuadratic() ) {
3470             int nb = elem->NbNodes() / 2;
3471             iAfter  = SMESH_MesherHelper::WrapIndex( iAfter, nb );
3472             iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
3473           }
3474           linkedNodes.insert( elem->GetNodeWrap( iAfter ));
3475           linkedNodes.insert( elem->GetNodeWrap( iBefore ));
3476         }
3477       }
3478     }
3479   }
3480 }
3481
3482 //=======================================================================
3483 //function : laplacianSmooth
3484 //purpose  : pulls theNode toward the center of surrounding nodes directly
3485 //           connected to that node along an element edge
3486 //=======================================================================
3487
3488 void laplacianSmooth(const SMDS_MeshNode*                 theNode,
3489                      const Handle(Geom_Surface)&          theSurface,
3490                      map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3491 {
3492   // find surrounding nodes
3493
3494   TIDSortedElemSet nodeSet;
3495   SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
3496
3497   // compute new coodrs
3498
3499   double coord[] = { 0., 0., 0. };
3500   TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3501   for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
3502     const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3503     if ( theSurface.IsNull() ) { // smooth in 3D
3504       coord[0] += node->X();
3505       coord[1] += node->Y();
3506       coord[2] += node->Z();
3507     }
3508     else { // smooth in 2D
3509       ASSERT( theUVMap.find( node ) != theUVMap.end() );
3510       gp_XY* uv = theUVMap[ node ];
3511       coord[0] += uv->X();
3512       coord[1] += uv->Y();
3513     }
3514   }
3515   int nbNodes = nodeSet.size();
3516   if ( !nbNodes )
3517     return;
3518   coord[0] /= nbNodes;
3519   coord[1] /= nbNodes;
3520
3521   if ( !theSurface.IsNull() ) {
3522     ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3523     theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3524     gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3525     coord[0] = p3d.X();
3526     coord[1] = p3d.Y();
3527     coord[2] = p3d.Z();
3528   }
3529   else
3530     coord[2] /= nbNodes;
3531
3532   // move node
3533
3534   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3535 }
3536
3537 //=======================================================================
3538 //function : centroidalSmooth
3539 //purpose  : pulls theNode toward the element-area-weighted centroid of the
3540 //           surrounding elements
3541 //=======================================================================
3542
3543 void centroidalSmooth(const SMDS_MeshNode*                 theNode,
3544                       const Handle(Geom_Surface)&          theSurface,
3545                       map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3546 {
3547   gp_XYZ aNewXYZ(0.,0.,0.);
3548   SMESH::Controls::Area anAreaFunc;
3549   double totalArea = 0.;
3550   int nbElems = 0;
3551
3552   // compute new XYZ
3553
3554   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3555   while ( elemIt->more() )
3556   {
3557     const SMDS_MeshElement* elem = elemIt->next();
3558     nbElems++;
3559
3560     gp_XYZ elemCenter(0.,0.,0.);
3561     SMESH::Controls::TSequenceOfXYZ aNodePoints;
3562     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3563     int nn = elem->NbNodes();
3564     if(elem->IsQuadratic()) nn = nn/2;
3565     int i=0;
3566     //while ( itN->more() ) {
3567     while ( i<nn ) {
3568       const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3569       i++;
3570       gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3571       aNodePoints.push_back( aP );
3572       if ( !theSurface.IsNull() ) { // smooth in 2D
3573         ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3574         gp_XY* uv = theUVMap[ aNode ];
3575         aP.SetCoord( uv->X(), uv->Y(), 0. );
3576       }
3577       elemCenter += aP;
3578     }
3579     double elemArea = anAreaFunc.GetValue( aNodePoints );
3580     totalArea += elemArea;
3581     elemCenter /= nn;
3582     aNewXYZ += elemCenter * elemArea;
3583   }
3584   aNewXYZ /= totalArea;
3585   if ( !theSurface.IsNull() ) {
3586     theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3587     aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3588   }
3589
3590   // move node
3591
3592   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3593 }
3594
3595 //=======================================================================
3596 //function : getClosestUV
3597 //purpose  : return UV of closest projection
3598 //=======================================================================
3599
3600 static bool getClosestUV (Extrema_GenExtPS& projector,
3601                           const gp_Pnt&     point,
3602                           gp_XY &           result)
3603 {
3604   projector.Perform( point );
3605   if ( projector.IsDone() ) {
3606     double u, v, minVal = DBL_MAX;
3607     for ( int i = projector.NbExt(); i > 0; i-- )
3608       if ( projector.SquareDistance( i ) < minVal ) {
3609         minVal = projector.SquareDistance( i );
3610         projector.Point( i ).Parameter( u, v );
3611       }
3612     result.SetCoord( u, v );
3613     return true;
3614   }
3615   return false;
3616 }
3617
3618 //=======================================================================
3619 //function : Smooth
3620 //purpose  : Smooth theElements during theNbIterations or until a worst
3621 //           element has aspect ratio <= theTgtAspectRatio.
3622 //           Aspect Ratio varies in range [1.0, inf].
3623 //           If theElements is empty, the whole mesh is smoothed.
3624 //           theFixedNodes contains additionally fixed nodes. Nodes built
3625 //           on edges and boundary nodes are always fixed.
3626 //=======================================================================
3627
3628 void SMESH_MeshEditor::Smooth (TIDSortedElemSet &          theElems,
3629                                set<const SMDS_MeshNode*> & theFixedNodes,
3630                                const SmoothMethod          theSmoothMethod,
3631                                const int                   theNbIterations,
3632                                double                      theTgtAspectRatio,
3633                                const bool                  the2D)
3634 {
3635   ClearLastCreated();
3636
3637   if ( theTgtAspectRatio < 1.0 )
3638     theTgtAspectRatio = 1.0;
3639
3640   const double disttol = 1.e-16;
3641
3642   SMESH::Controls::AspectRatio aQualityFunc;
3643
3644   SMESHDS_Mesh* aMesh = GetMeshDS();
3645
3646   if ( theElems.empty() ) {
3647     // add all faces to theElems
3648     SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3649     while ( fIt->more() ) {
3650       const SMDS_MeshElement* face = fIt->next();
3651       theElems.insert( theElems.end(), face );
3652     }
3653   }
3654   // get all face ids theElems are on
3655   set< int > faceIdSet;
3656   TIDSortedElemSet::iterator itElem;
3657   if ( the2D )
3658     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3659       int fId = FindShape( *itElem );
3660       // check that corresponding submesh exists and a shape is face
3661       if (fId &&
3662           faceIdSet.find( fId ) == faceIdSet.end() &&
3663           aMesh->MeshElements( fId )) {
3664         TopoDS_Shape F = aMesh->IndexToShape( fId );
3665         if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3666           faceIdSet.insert( fId );
3667       }
3668     }
3669   faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3670
3671   // ===============================================
3672   // smooth elements on each TopoDS_Face separately
3673   // ===============================================
3674
3675   SMESH_MesherHelper helper( *GetMesh() );
3676
3677   set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treat 0 fId at the end
3678   for ( ; fId != faceIdSet.rend(); ++fId )
3679   {
3680     // get face surface and submesh
3681     Handle(Geom_Surface) surface;
3682     SMESHDS_SubMesh* faceSubMesh = 0;
3683     TopoDS_Face face;
3684     double fToler2 = 0;
3685     double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3686     bool isUPeriodic = false, isVPeriodic = false;
3687     if ( *fId )
3688     {
3689       face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3690       surface = BRep_Tool::Surface( face );
3691       faceSubMesh = aMesh->MeshElements( *fId );
3692       fToler2 = BRep_Tool::Tolerance( face );
3693       fToler2 *= fToler2 * 10.;
3694       isUPeriodic = surface->IsUPeriodic();
3695       // if ( isUPeriodic )
3696       //   surface->UPeriod();
3697       isVPeriodic = surface->IsVPeriodic();
3698       // if ( isVPeriodic )
3699       //   surface->VPeriod();
3700       surface->Bounds( u1, u2, v1, v2 );
3701       helper.SetSubShape( face );
3702     }
3703     // ---------------------------------------------------------
3704     // for elements on a face, find movable and fixed nodes and
3705     // compute UV for them
3706     // ---------------------------------------------------------
3707     bool checkBoundaryNodes = false;
3708     bool isQuadratic = false;
3709     set<const SMDS_MeshNode*> setMovableNodes;
3710     map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
3711     list< gp_XY > listUV; // uvs the 2 uvMaps refer to
3712     list< const SMDS_MeshElement* > elemsOnFace;
3713
3714     Extrema_GenExtPS projector;
3715     GeomAdaptor_Surface surfAdaptor;
3716     if ( !surface.IsNull() ) {
3717       surfAdaptor.Load( surface );
3718       projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
3719     }
3720     int nbElemOnFace = 0;
3721     itElem = theElems.begin();
3722     // loop on not yet smoothed elements: look for elems on a face
3723     while ( itElem != theElems.end() )
3724     {
3725       if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
3726         break; // all elements found
3727
3728       const SMDS_MeshElement* elem = *itElem;
3729       if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
3730            ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
3731         ++itElem;
3732         continue;
3733       }
3734       elemsOnFace.push_back( elem );
3735       theElems.erase( itElem++ );
3736       nbElemOnFace++;
3737
3738       if ( !isQuadratic )
3739         isQuadratic = elem->IsQuadratic();
3740
3741       // get movable nodes of elem
3742       const SMDS_MeshNode* node;
3743       SMDS_TypeOfPosition posType;
3744       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3745       int nn = 0, nbn =  elem->NbNodes();
3746       if(elem->IsQuadratic())
3747         nbn = nbn/2;
3748       while ( nn++ < nbn ) {
3749         node = static_cast<const SMDS_MeshNode*>( itN->next() );
3750         const SMDS_PositionPtr& pos = node->GetPosition();
3751         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3752         if (posType != SMDS_TOP_EDGE &&
3753             posType != SMDS_TOP_VERTEX &&
3754             theFixedNodes.find( node ) == theFixedNodes.end())
3755         {
3756           // check if all faces around the node are on faceSubMesh
3757           // because a node on edge may be bound to face
3758           bool all = true;
3759           if ( faceSubMesh ) {
3760             SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3761             while ( eIt->more() && all ) {
3762               const SMDS_MeshElement* e = eIt->next();
3763               all = faceSubMesh->Contains( e );
3764             }
3765           }
3766           if ( all )
3767             setMovableNodes.insert( node );
3768           else
3769             checkBoundaryNodes = true;
3770         }
3771         if ( posType == SMDS_TOP_3DSPACE )
3772           checkBoundaryNodes = true;
3773       }
3774
3775       if ( surface.IsNull() )
3776         continue;
3777
3778       // get nodes to check UV
3779       list< const SMDS_MeshNode* > uvCheckNodes;
3780       const SMDS_MeshNode* nodeInFace = 0;
3781       itN = elem->nodesIterator();
3782       nn = 0; nbn =  elem->NbNodes();
3783       if(elem->IsQuadratic())
3784         nbn = nbn/2;
3785       while ( nn++ < nbn ) {
3786         node = static_cast<const SMDS_MeshNode*>( itN->next() );
3787         if ( node->GetPosition()->GetDim() == 2 )
3788           nodeInFace = node;
3789         if ( uvMap.find( node ) == uvMap.end() )
3790           uvCheckNodes.push_back( node );
3791         // add nodes of elems sharing node
3792         //         SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3793         //         while ( eIt->more() ) {
3794         //           const SMDS_MeshElement* e = eIt->next();
3795         //           if ( e != elem ) {
3796         //             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3797         //             while ( nIt->more() ) {
3798         //               const SMDS_MeshNode* n =
3799         //                 static_cast<const SMDS_MeshNode*>( nIt->next() );
3800         //               if ( uvMap.find( n ) == uvMap.end() )
3801         //                 uvCheckNodes.push_back( n );
3802         //             }
3803         //           }
3804         //         }
3805       }
3806       // check UV on face
3807       list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
3808       for ( ; n != uvCheckNodes.end(); ++n ) {
3809         node = *n;
3810         gp_XY uv( 0, 0 );
3811         const SMDS_PositionPtr& pos = node->GetPosition();
3812         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3813         // get existing UV
3814         if ( pos )
3815         {
3816           bool toCheck = true;
3817           uv = helper.GetNodeUV( face, node, nodeInFace, &toCheck );
3818         }
3819         // compute not existing UV
3820         bool project = ( posType == SMDS_TOP_3DSPACE );
3821         // double dist1 = DBL_MAX, dist2 = 0;
3822         // if ( posType != SMDS_TOP_3DSPACE ) {
3823         //   dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
3824         //   project = dist1 > fToler2;
3825         // }
3826         if ( project ) { // compute new UV
3827           gp_XY newUV;
3828           gp_Pnt pNode = SMESH_NodeXYZ( node );
3829           if ( !getClosestUV( projector, pNode, newUV )) {
3830             MESSAGE("Node Projection Failed " << node);
3831           }
3832           else {
3833             if ( isUPeriodic )
3834               newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
3835             if ( isVPeriodic )
3836               newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
3837             // check new UV
3838             // if ( posType != SMDS_TOP_3DSPACE )
3839             //   dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
3840             // if ( dist2 < dist1 )
3841             uv = newUV;
3842           }
3843         }
3844         // store UV in the map
3845         listUV.push_back( uv );
3846         uvMap.insert( make_pair( node, &listUV.back() ));
3847       }
3848     } // loop on not yet smoothed elements
3849
3850     if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
3851       checkBoundaryNodes = true;
3852
3853     // fix nodes on mesh boundary
3854
3855     if ( checkBoundaryNodes ) {
3856       map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
3857       map< SMESH_TLink, int >::iterator link_nb;
3858       // put all elements links to linkNbMap
3859       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3860       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3861         const SMDS_MeshElement* elem = (*elemIt);
3862         int nbn =  elem->NbCornerNodes();
3863         // loop on elem links: insert them in linkNbMap
3864         for ( int iN = 0; iN < nbn; ++iN ) {
3865           const SMDS_MeshNode* n1 = elem->GetNode( iN );
3866           const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
3867           SMESH_TLink link( n1, n2 );
3868           link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
3869           link_nb->second++;
3870         }
3871       }
3872       // remove nodes that are in links encountered only once from setMovableNodes
3873       for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
3874         if ( link_nb->second == 1 ) {
3875           setMovableNodes.erase( link_nb->first.node1() );
3876           setMovableNodes.erase( link_nb->first.node2() );
3877         }
3878       }
3879     }
3880
3881     // -----------------------------------------------------
3882     // for nodes on seam edge, compute one more UV ( uvMap2 );
3883     // find movable nodes linked to nodes on seam and which
3884     // are to be smoothed using the second UV ( uvMap2 )
3885     // -----------------------------------------------------
3886
3887     set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
3888     if ( !surface.IsNull() ) {
3889       TopExp_Explorer eExp( face, TopAbs_EDGE );
3890       for ( ; eExp.More(); eExp.Next() ) {
3891         TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
3892         if ( !BRep_Tool::IsClosed( edge, face ))
3893           continue;
3894         SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
3895         if ( !sm ) continue;
3896         // find out which parameter varies for a node on seam
3897         double f,l;
3898         gp_Pnt2d uv1, uv2;
3899         Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3900         if ( pcurve.IsNull() ) continue;
3901         uv1 = pcurve->Value( f );
3902         edge.Reverse();
3903         pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3904         if ( pcurve.IsNull() ) continue;
3905         uv2 = pcurve->Value( f );
3906         int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
3907         // assure uv1 < uv2
3908         if ( uv1.Coord( iPar ) > uv2.Coord( iPar ))
3909           std::swap( uv1, uv2 );
3910         // get nodes on seam and its vertices
3911         list< const SMDS_MeshNode* > seamNodes;
3912         SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
3913         while ( nSeamIt->more() ) {
3914           const SMDS_MeshNode* node = nSeamIt->next();
3915           if ( !isQuadratic || !IsMedium( node ))
3916             seamNodes.push_back( node );
3917         }
3918         TopExp_Explorer vExp( edge, TopAbs_VERTEX );
3919         for ( ; vExp.More(); vExp.Next() ) {
3920           sm = aMesh->MeshElements( vExp.Current() );
3921           if ( sm ) {
3922             nSeamIt = sm->GetNodes();
3923             while ( nSeamIt->more() )
3924               seamNodes.push_back( nSeamIt->next() );
3925           }
3926         }
3927         // loop on nodes on seam
3928         list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
3929         for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
3930           const SMDS_MeshNode* nSeam = *noSeIt;
3931           map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
3932           if ( n_uv == uvMap.end() )
3933             continue;
3934           // set the first UV
3935           n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
3936           // set the second UV
3937           listUV.push_back( *n_uv->second );
3938           listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
3939           if ( uvMap2.empty() )
3940             uvMap2 = uvMap; // copy the uvMap contents
3941           uvMap2[ nSeam ] = &listUV.back();
3942
3943           // collect movable nodes linked to ones on seam in nodesNearSeam
3944           SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
3945           while ( eIt->more() ) {
3946             const SMDS_MeshElement* e = eIt->next();
3947             int nbUseMap1 = 0, nbUseMap2 = 0;
3948             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3949             int nn = 0, nbn =  e->NbNodes();
3950             if(e->IsQuadratic()) nbn = nbn/2;
3951             while ( nn++ < nbn )
3952             {
3953               const SMDS_MeshNode* n =
3954                 static_cast<const SMDS_MeshNode*>( nIt->next() );
3955               if (n == nSeam ||
3956                   setMovableNodes.find( n ) == setMovableNodes.end() )
3957                 continue;
3958               // add only nodes being closer to uv2 than to uv1
3959               // gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
3960               //              0.5 * ( n->Y() + nSeam->Y() ),
3961               //              0.5 * ( n->Z() + nSeam->Z() ));
3962               // gp_XY uv;
3963               // getClosestUV( projector, pMid, uv );
3964               double x = uvMap[ n ]->Coord( iPar );
3965               if ( Abs( uv1.Coord( iPar ) - x ) >
3966                    Abs( uv2.Coord( iPar ) - x )) {
3967                 nodesNearSeam.insert( n );
3968                 nbUseMap2++;
3969               }
3970               else
3971                 nbUseMap1++;
3972             }
3973             // for centroidalSmooth all element nodes must
3974             // be on one side of a seam
3975             if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
3976               SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3977               nn = 0;
3978               while ( nn++ < nbn ) {
3979                 const SMDS_MeshNode* n =
3980                   static_cast<const SMDS_MeshNode*>( nIt->next() );
3981                 setMovableNodes.erase( n );
3982               }
3983             }
3984           }
3985         } // loop on nodes on seam
3986       } // loop on edge of a face
3987     } // if ( !face.IsNull() )
3988
3989     if ( setMovableNodes.empty() ) {
3990       MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
3991       continue; // goto next face
3992     }
3993
3994     // -------------
3995     // SMOOTHING //
3996     // -------------
3997
3998     int it = -1;
3999     double maxRatio = -1., maxDisplacement = -1.;
4000     set<const SMDS_MeshNode*>::iterator nodeToMove;
4001     for ( it = 0; it < theNbIterations; it++ ) {
4002       maxDisplacement = 0.;
4003       nodeToMove = setMovableNodes.begin();
4004       for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4005         const SMDS_MeshNode* node = (*nodeToMove);
4006         gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
4007
4008         // smooth
4009         bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
4010         if ( theSmoothMethod == LAPLACIAN )
4011           laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
4012         else
4013           centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
4014
4015         // node displacement
4016         gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
4017         Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
4018         if ( aDispl > maxDisplacement )
4019           maxDisplacement = aDispl;
4020       }
4021       // no node movement => exit
4022       //if ( maxDisplacement < 1.e-16 ) {
4023       if ( maxDisplacement < disttol ) {
4024         MESSAGE("-- no node movement --");
4025         break;
4026       }
4027
4028       // check elements quality
4029       maxRatio  = 0;
4030       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4031       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4032         const SMDS_MeshElement* elem = (*elemIt);
4033         if ( !elem || elem->GetType() != SMDSAbs_Face )
4034           continue;
4035         SMESH::Controls::TSequenceOfXYZ aPoints;
4036         if ( aQualityFunc.GetPoints( elem, aPoints )) {
4037           double aValue = aQualityFunc.GetValue( aPoints );
4038           if ( aValue > maxRatio )
4039             maxRatio = aValue;
4040         }
4041       }
4042       if ( maxRatio <= theTgtAspectRatio ) {
4043         //MESSAGE("-- quality achieved --");
4044         break;
4045       }
4046       if (it+1 == theNbIterations) {
4047         //MESSAGE("-- Iteration limit exceeded --");
4048       }
4049     } // smoothing iterations
4050
4051     // MESSAGE(" Face id: " << *fId <<
4052     //         " Nb iterstions: " << it <<
4053     //         " Displacement: " << maxDisplacement <<
4054     //         " Aspect Ratio " << maxRatio);
4055
4056     // ---------------------------------------
4057     // new nodes positions are computed,
4058     // record movement in DS and set new UV
4059     // ---------------------------------------
4060     nodeToMove = setMovableNodes.begin();
4061     for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4062       SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
4063       aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
4064       map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
4065       if ( node_uv != uvMap.end() ) {
4066         gp_XY* uv = node_uv->second;
4067         node->SetPosition
4068           ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
4069       }
4070     }
4071
4072     // move medium nodes of quadratic elements
4073     if ( isQuadratic )
4074     {
4075       vector<const SMDS_MeshNode*> nodes;
4076       bool checkUV;
4077       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4078       for ( ; elemIt != elemsOnFace.end(); ++elemIt )
4079       {
4080         const SMDS_MeshElement* QF = *elemIt;
4081         if ( QF->IsQuadratic() )
4082         {
4083           nodes.assign( SMDS_MeshElement::iterator( QF->interlacedNodesIterator() ),
4084                         SMDS_MeshElement::iterator() );
4085           nodes.push_back( nodes[0] );
4086           gp_Pnt xyz;
4087           for (size_t i = 1; i < nodes.size(); i += 2 ) // i points to a medium node
4088           {
4089             if ( !surface.IsNull() )
4090             {
4091               gp_XY uv1 = helper.GetNodeUV( face, nodes[i-1], nodes[i+1], &checkUV );
4092               gp_XY uv2 = helper.GetNodeUV( face, nodes[i+1], nodes[i-1], &checkUV );
4093               gp_XY uv  = helper.GetMiddleUV( surface, uv1, uv2 );
4094               xyz = surface->Value( uv.X(), uv.Y() );
4095             }
4096             else {
4097               xyz = 0.5 * ( SMESH_NodeXYZ( nodes[i-1] ) + SMESH_NodeXYZ( nodes[i+1] ));
4098             }
4099             if (( SMESH_NodeXYZ( nodes[i] ) - xyz.XYZ() ).Modulus() > disttol )
4100               // we have to move a medium node
4101               aMesh->MoveNode( nodes[i], xyz.X(), xyz.Y(), xyz.Z() );
4102           }
4103         }
4104       }
4105     }
4106
4107   } // loop on face ids
4108
4109 }
4110
4111 namespace
4112 {
4113   //=======================================================================
4114   //function : isReverse
4115   //purpose  : Return true if normal of prevNodes is not co-directied with
4116   //           gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
4117   //           iNotSame is where prevNodes and nextNodes are different.
4118   //           If result is true then future volume orientation is OK
4119   //=======================================================================
4120
4121   bool isReverse(const SMDS_MeshElement*             face,
4122                  const vector<const SMDS_MeshNode*>& prevNodes,
4123                  const vector<const SMDS_MeshNode*>& nextNodes,
4124                  const int                           iNotSame)
4125   {
4126
4127     SMESH_NodeXYZ pP = prevNodes[ iNotSame ];
4128     SMESH_NodeXYZ pN = nextNodes[ iNotSame ];
4129     gp_XYZ extrDir( pN - pP ), faceNorm;
4130     SMESH_MeshAlgos::FaceNormal( face, faceNorm, /*normalized=*/false );
4131
4132     return faceNorm * extrDir < 0.0;
4133   }
4134
4135   //================================================================================
4136   /*!
4137    * \brief Assure that theElemSets[0] holds elements, not nodes
4138    */
4139   //================================================================================
4140
4141   void setElemsFirst( TIDSortedElemSet theElemSets[2] )
4142   {
4143     if ( !theElemSets[0].empty() &&
4144          (*theElemSets[0].begin())->GetType() == SMDSAbs_Node )
4145     {
4146       std::swap( theElemSets[0], theElemSets[1] );
4147     }
4148     else if ( !theElemSets[1].empty() &&
4149               (*theElemSets[1].begin())->GetType() != SMDSAbs_Node )
4150     {
4151       std::swap( theElemSets[0], theElemSets[1] );
4152     }
4153   }
4154 }
4155
4156 //=======================================================================
4157 /*!
4158  * \brief Create elements by sweeping an element
4159  * \param elem - element to sweep
4160  * \param newNodesItVec - nodes generated from each node of the element
4161  * \param newElems - generated elements
4162  * \param nbSteps - number of sweeping steps
4163  * \param srcElements - to append elem for each generated element
4164  */
4165 //=======================================================================
4166
4167 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement*               elem,
4168                                     const vector<TNodeOfNodeListMapItr> & newNodesItVec,
4169                                     list<const SMDS_MeshElement*>&        newElems,
4170                                     const size_t                          nbSteps,
4171                                     SMESH_SequenceOfElemPtr&              srcElements)
4172 {
4173   SMESHDS_Mesh* aMesh = GetMeshDS();
4174
4175   const int           nbNodes = elem->NbNodes();
4176   const int         nbCorners = elem->NbCornerNodes();
4177   SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
4178                                                           polyhedron creation !!! */
4179   // Loop on elem nodes:
4180   // find new nodes and detect same nodes indices
4181   vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
4182   vector<const SMDS_MeshNode*> prevNod( nbNodes );
4183   vector<const SMDS_MeshNode*> nextNod( nbNodes );
4184   vector<const SMDS_MeshNode*> midlNod( nbNodes );
4185
4186   int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
4187   vector<int> sames(nbNodes);
4188   vector<bool> isSingleNode(nbNodes);
4189
4190   for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4191     TNodeOfNodeListMapItr                        nnIt = newNodesItVec[ iNode ];
4192     const SMDS_MeshNode*                         node = nnIt->first;
4193     const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
4194     if ( listNewNodes.empty() )
4195       return;
4196
4197     itNN   [ iNode ] = listNewNodes.begin();
4198     prevNod[ iNode ] = node;
4199     nextNod[ iNode ] = listNewNodes.front();
4200
4201     isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
4202                                                              corner node of linear */
4203     if ( prevNod[ iNode ] != nextNod [ iNode ])
4204       nbDouble += !isSingleNode[iNode];
4205
4206     if( iNode < nbCorners ) { // check corners only
4207       if ( prevNod[ iNode ] == nextNod [ iNode ])
4208         sames[nbSame++] = iNode;
4209       else
4210         iNotSameNode = iNode;
4211     }
4212   }
4213
4214   if ( nbSame == nbNodes || nbSame > 2) {
4215     MESSAGE( " Too many same nodes of element " << elem->GetID() );
4216     return;
4217   }
4218
4219   if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
4220   {
4221     // fix nodes order to have bottom normal external
4222     if ( baseType == SMDSEntity_Polygon )
4223     {
4224       std::reverse( itNN.begin(), itNN.end() );
4225       std::reverse( prevNod.begin(), prevNod.end() );
4226       std::reverse( midlNod.begin(), midlNod.end() );
4227       std::reverse( nextNod.begin(), nextNod.end() );
4228       std::reverse( isSingleNode.begin(), isSingleNode.end() );
4229     }
4230     else
4231     {
4232       const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType, nbNodes );
4233       SMDS_MeshCell::applyInterlace( ind, itNN );
4234       SMDS_MeshCell::applyInterlace( ind, prevNod );
4235       SMDS_MeshCell::applyInterlace( ind, nextNod );
4236       SMDS_MeshCell::applyInterlace( ind, midlNod );
4237       SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4238       if ( nbSame > 0 )
4239       {
4240         sames[nbSame] = iNotSameNode;
4241         for ( int j = 0; j <= nbSame; ++j )
4242           for ( size_t i = 0; i < ind.size(); ++i )
4243             if ( ind[i] == sames[j] )
4244             {
4245               sames[j] = i;
4246               break;
4247             }
4248         iNotSameNode = sames[nbSame];
4249       }
4250     }
4251   }
4252   else if ( elem->GetType() == SMDSAbs_Edge )
4253   {
4254     // orient a new face same as adjacent one
4255     int i1, i2;
4256     const SMDS_MeshElement* e;
4257     TIDSortedElemSet dummy;
4258     if (( e = SMESH_MeshAlgos::FindFaceInSet( nextNod[0], prevNod[0], dummy,dummy, &i1, &i2 )) ||
4259         ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[1], nextNod[1], dummy,dummy, &i1, &i2 )) ||
4260         ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[0], prevNod[1], dummy,dummy, &i1, &i2 )))
4261     {
4262       // there is an adjacent face, check order of nodes in it
4263       bool sameOrder = ( Abs( i2 - i1 ) == 1 ) ? ( i2 > i1 ) : ( i2 < i1 );
4264       if ( sameOrder )
4265       {
4266         std::swap( itNN[0],    itNN[1] );
4267         std::swap( prevNod[0], prevNod[1] );
4268         std::swap( nextNod[0], nextNod[1] );
4269         std::swap( isSingleNode[0], isSingleNode[1] );
4270         if ( nbSame > 0 )
4271           sames[0] = 1 - sames[0];
4272         iNotSameNode = 1 - iNotSameNode;
4273       }
4274     }
4275   }
4276
4277   int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
4278   if ( nbSame > 0 ) {
4279     iSameNode    = sames[ nbSame-1 ];
4280     iBeforeSame  = ( iSameNode + nbCorners - 1 ) % nbCorners;
4281     iAfterSame   = ( iSameNode + 1 ) % nbCorners;
4282     iOpposSame   = ( iSameNode - 2 < 0  ? iSameNode + 2 : iSameNode - 2 );
4283   }
4284
4285   if ( baseType == SMDSEntity_Polygon )
4286   {
4287     if      ( nbNodes == 3 ) baseType = SMDSEntity_Triangle;
4288     else if ( nbNodes == 4 ) baseType = SMDSEntity_Quadrangle;
4289   }
4290   else if ( baseType == SMDSEntity_Quad_Polygon )
4291   {
4292     if      ( nbNodes == 6 ) baseType = SMDSEntity_Quad_Triangle;
4293     else if ( nbNodes == 8 ) baseType = SMDSEntity_Quad_Quadrangle;
4294   }
4295
4296   // make new elements
4297   for ( size_t iStep = 0; iStep < nbSteps; iStep++ )
4298   {
4299     // get next nodes
4300     for ( iNode = 0; iNode < nbNodes; iNode++ )
4301     {
4302       midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
4303       nextNod[ iNode ] = *itNN[ iNode ]++;
4304     }
4305
4306     SMDS_MeshElement* aNewElem = 0;
4307     /*if(!elem->IsPoly())*/ {
4308       switch ( baseType ) {
4309       case SMDSEntity_0D:
4310       case SMDSEntity_Node: { // sweep NODE
4311         if ( nbSame == 0 ) {
4312           if ( isSingleNode[0] )
4313             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
4314           else
4315             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
4316         }
4317         else
4318           return;
4319         break;
4320       }
4321       case SMDSEntity_Edge: { // sweep EDGE
4322         if ( nbDouble == 0 )
4323         {
4324           if ( nbSame == 0 ) // ---> quadrangle
4325             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4326                                       nextNod[ 1 ], nextNod[ 0 ] );
4327           else               // ---> triangle
4328             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4329                                       nextNod[ iNotSameNode ] );
4330         }
4331         else                 // ---> polygon
4332         {
4333           vector<const SMDS_MeshNode*> poly_nodes;
4334           poly_nodes.push_back( prevNod[0] );
4335           poly_nodes.push_back( prevNod[1] );
4336           if ( prevNod[1] != nextNod[1] )
4337           {
4338             if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
4339             poly_nodes.push_back( nextNod[1] );
4340           }
4341           if ( prevNod[0] != nextNod[0] )
4342           {
4343             poly_nodes.push_back( nextNod[0] );
4344             if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
4345           }
4346           switch ( poly_nodes.size() ) {
4347           case 3:
4348             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
4349             break;
4350           case 4:
4351             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
4352                                        poly_nodes[ 2 ], poly_nodes[ 3 ]);
4353             break;
4354           default:
4355             aNewElem = aMesh->AddPolygonalFace (poly_nodes);
4356           }
4357         }
4358         break;
4359       }
4360       case SMDSEntity_Triangle: // TRIANGLE --->
4361       {
4362         if ( nbDouble > 0 ) break;
4363         if ( nbSame == 0 )       // ---> pentahedron
4364           aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4365                                        nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
4366
4367         else if ( nbSame == 1 )  // ---> pyramid
4368           aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4369                                        nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
4370                                        nextNod[ iSameNode ]);
4371
4372         else // 2 same nodes:       ---> tetrahedron
4373           aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4374                                        nextNod[ iNotSameNode ]);
4375         break;
4376       }
4377       case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
4378       {
4379         if ( nbSame == 2 )
4380           return;
4381         if ( nbDouble+nbSame == 2 )
4382         {
4383           if(nbSame==0) {      // ---> quadratic quadrangle
4384             aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4385                                       prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
4386           }
4387           else { //(nbSame==1) // ---> quadratic triangle
4388             if(sames[0]==2) {
4389               return; // medium node on axis
4390             }
4391             else if(sames[0]==0)
4392               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1],
4393                                         prevNod[2], midlNod[1], nextNod[2] );
4394             else // sames[0]==1
4395               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[0],
4396                                         prevNod[2], nextNod[2], midlNod[0]);
4397           }
4398         }
4399         else if ( nbDouble == 3 )
4400         {
4401           if ( nbSame == 0 ) {  // ---> bi-quadratic quadrangle
4402             aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4403                                       prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
4404           }
4405         }
4406         else
4407           return;
4408         break;
4409       }
4410       case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
4411         if ( nbDouble > 0 ) break;
4412
4413         if ( nbSame == 0 )       // ---> hexahedron
4414           aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
4415                                        nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
4416
4417         else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
4418           aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4419                                        nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
4420                                        nextNod[ iSameNode ]);
4421           newElems.push_back( aNewElem );
4422           aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ],  prevNod[ iOpposSame ],
4423                                        prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
4424                                        nextNod[ iOpposSame ],  nextNod[ iBeforeSame ] );
4425         }
4426         else if ( nbSame == 2 ) { // ---> pentahedron
4427           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
4428             // iBeforeSame is same too
4429             aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
4430                                          nextNod[ iOpposSame ],  prevNod[ iSameNode ],
4431                                          prevNod[ iAfterSame ],  nextNod[ iAfterSame ]);
4432           else
4433             // iAfterSame is same too
4434             aNewElem = aMesh->AddVolume (prevNod[ iSameNode ],   prevNod[ iBeforeSame ],
4435                                          nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
4436                                          prevNod[ iOpposSame ],  nextNod[ iOpposSame ]);
4437         }
4438         break;
4439       }
4440       case SMDSEntity_Quad_Triangle:  // sweep (Bi)Quadratic TRIANGLE --->
4441       case SMDSEntity_BiQuad_Triangle: /* ??? */ {
4442         if ( nbDouble+nbSame != 3 ) break;
4443         if(nbSame==0) {
4444           // --->  pentahedron with 15 nodes
4445           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4446                                        nextNod[0], nextNod[1], nextNod[2],
4447                                        prevNod[3], prevNod[4], prevNod[5],
4448                                        nextNod[3], nextNod[4], nextNod[5],
4449                                        midlNod[0], midlNod[1], midlNod[2]);
4450         }
4451         else if(nbSame==1) {
4452           // --->  2d order pyramid of 13 nodes
4453           int apex = iSameNode;
4454           int i0 = ( apex + 1 ) % nbCorners;
4455           int i1 = ( apex - 1 + nbCorners ) % nbCorners;
4456           int i0a = apex + 3;
4457           int i1a = i1 + 3;
4458           int i01 = i0 + 3;
4459           aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
4460                                       nextNod[i0], nextNod[i1], prevNod[apex],
4461                                       prevNod[i01], midlNod[i0],
4462                                       nextNod[i01], midlNod[i1],
4463                                       prevNod[i1a], prevNod[i0a],
4464                                       nextNod[i0a], nextNod[i1a]);
4465         }
4466         else if(nbSame==2) {
4467           // --->  2d order tetrahedron of 10 nodes
4468           int n1 = iNotSameNode;
4469           int n2 = ( n1 + 1             ) % nbCorners;
4470           int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
4471           int n12 = n1 + 3;
4472           int n23 = n2 + 3;
4473           int n31 = n3 + 3;
4474           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
4475                                        prevNod[n12], prevNod[n23], prevNod[n31],
4476                                        midlNod[n1], nextNod[n12], nextNod[n31]);
4477         }
4478         break;
4479       }
4480       case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
4481         if( nbSame == 0 ) {
4482           if ( nbDouble != 4 ) break;
4483           // --->  hexahedron with 20 nodes
4484           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4485                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4486                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4487                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4488                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
4489         }
4490         else if(nbSame==1) {
4491           // ---> pyramid + pentahedron - can not be created since it is needed
4492           // additional middle node at the center of face
4493           //INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
4494           return;
4495         }
4496         else if( nbSame == 2 ) {
4497           if ( nbDouble != 2 ) break;
4498           // --->  2d order Pentahedron with 15 nodes
4499           int n1,n2,n4,n5;
4500           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
4501             // iBeforeSame is same too
4502             n1 = iBeforeSame;
4503             n2 = iOpposSame;
4504             n4 = iSameNode;
4505             n5 = iAfterSame;
4506           }
4507           else {
4508             // iAfterSame is same too
4509             n1 = iSameNode;
4510             n2 = iBeforeSame;
4511             n4 = iAfterSame;
4512             n5 = iOpposSame;
4513           }
4514           int n12 = n2 + 4;
4515           int n45 = n4 + 4;
4516           int n14 = n1 + 4;
4517           int n25 = n5 + 4;
4518           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
4519                                        prevNod[n4], prevNod[n5], nextNod[n5],
4520                                        prevNod[n12], midlNod[n2], nextNod[n12],
4521                                        prevNod[n45], midlNod[n5], nextNod[n45],
4522                                        prevNod[n14], prevNod[n25], nextNod[n25]);
4523         }
4524         break;
4525       }
4526       case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
4527
4528         if( nbSame == 0 && nbDouble == 9 ) {
4529           // --->  tri-quadratic hexahedron with 27 nodes
4530           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4531                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4532                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4533                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4534                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4535                                        prevNod[8], // bottom center
4536                                        midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4537                                        nextNod[8], // top center
4538                                        midlNod[8]);// elem center
4539         }
4540         else
4541         {
4542           return;
4543         }
4544         break;
4545       }
4546       case SMDSEntity_Polygon: { // sweep POLYGON
4547
4548         if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4549           // --->  hexagonal prism
4550           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4551                                        prevNod[3], prevNod[4], prevNod[5],
4552                                        nextNod[0], nextNod[1], nextNod[2],
4553                                        nextNod[3], nextNod[4], nextNod[5]);
4554         }
4555         break;
4556       }
4557       case SMDSEntity_Ball:
4558         return;
4559
4560       default:
4561         break;
4562       } // switch ( baseType )
4563     } // scope
4564
4565     if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4566     {
4567       if ( baseType != SMDSEntity_Polygon )
4568       {
4569         const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType,nbNodes);
4570         SMDS_MeshCell::applyInterlace( ind, prevNod );
4571         SMDS_MeshCell::applyInterlace( ind, nextNod );
4572         SMDS_MeshCell::applyInterlace( ind, midlNod );
4573         SMDS_MeshCell::applyInterlace( ind, itNN );
4574         SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4575         baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4576       }
4577       vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4578       vector<int> quantities (nbNodes + 2);
4579       polyedre_nodes.clear();
4580       quantities.clear();
4581
4582       // bottom of prism
4583       for (int inode = 0; inode < nbNodes; inode++)
4584         polyedre_nodes.push_back( prevNod[inode] );
4585       quantities.push_back( nbNodes );
4586
4587       // top of prism
4588       polyedre_nodes.push_back( nextNod[0] );
4589       for (int inode = nbNodes; inode-1; --inode )
4590         polyedre_nodes.push_back( nextNod[inode-1] );
4591       quantities.push_back( nbNodes );
4592
4593       // side faces
4594       // 3--6--2
4595       // |     |
4596       // 7     5
4597       // |     |
4598       // 0--4--1
4599       const int iQuad = elem->IsQuadratic();
4600       for (int iface = 0; iface < nbNodes; iface += 1+iQuad )
4601       {
4602         const int prevNbNodes = polyedre_nodes.size(); // to detect degenerated face
4603         int inextface = (iface+1+iQuad) % nbNodes;
4604         int imid      = (iface+1) % nbNodes;
4605         polyedre_nodes.push_back( prevNod[inextface] );         // 0
4606         if ( iQuad ) polyedre_nodes.push_back( prevNod[imid] ); // 4
4607         polyedre_nodes.push_back( prevNod[iface] );             // 1
4608         if ( prevNod[iface] != nextNod[iface] ) // 1 != 2
4609         {
4610           if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]); // 5
4611           polyedre_nodes.push_back( nextNod[iface] );                         // 2
4612         }
4613         if ( iQuad ) polyedre_nodes.push_back( nextNod[imid] );               // 6
4614         if ( prevNod[inextface] != nextNod[inextface] ) // 0 != 3
4615         {
4616           polyedre_nodes.push_back( nextNod[inextface] );                            // 3
4617           if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);// 7
4618         }
4619         const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4620         if ( nbFaceNodes > 2 )
4621           quantities.push_back( nbFaceNodes );
4622         else // degenerated face
4623           polyedre_nodes.resize( prevNbNodes );
4624       }
4625       aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4626
4627     } // try to create a polyherdal prism
4628
4629     if ( aNewElem ) {
4630       newElems.push_back( aNewElem );
4631       myLastCreatedElems.push_back(aNewElem);
4632       srcElements.push_back( elem );
4633     }
4634
4635     // set new prev nodes
4636     for ( iNode = 0; iNode < nbNodes; iNode++ )
4637       prevNod[ iNode ] = nextNod[ iNode ];
4638
4639   } // loop on steps
4640 }
4641
4642 //=======================================================================
4643 /*!
4644  * \brief Create 1D and 2D elements around swept elements
4645  * \param mapNewNodes - source nodes and ones generated from them
4646  * \param newElemsMap - source elements and ones generated from them
4647  * \param elemNewNodesMap - nodes generated from each node of each element
4648  * \param elemSet - all swept elements
4649  * \param nbSteps - number of sweeping steps
4650  * \param srcElements - to append elem for each generated element
4651  */
4652 //=======================================================================
4653
4654 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap &     mapNewNodes,
4655                                   TTElemOfElemListMap &    newElemsMap,
4656                                   TElemOfVecOfNnlmiMap &   elemNewNodesMap,
4657                                   TIDSortedElemSet&        elemSet,
4658                                   const int                nbSteps,
4659                                   SMESH_SequenceOfElemPtr& srcElements)
4660 {
4661   ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4662   SMESHDS_Mesh* aMesh = GetMeshDS();
4663
4664   // Find nodes belonging to only one initial element - sweep them into edges.
4665
4666   TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4667   for ( ; nList != mapNewNodes.end(); nList++ )
4668   {
4669     const SMDS_MeshNode* node =
4670       static_cast<const SMDS_MeshNode*>( nList->first );
4671     if ( newElemsMap.count( node ))
4672       continue; // node was extruded into edge
4673     SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4674     int nbInitElems = 0;
4675     const SMDS_MeshElement* el = 0;
4676     SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4677     while ( eIt->more() && nbInitElems < 2 ) {
4678       const SMDS_MeshElement* e = eIt->next();
4679       SMDSAbs_ElementType  type = e->GetType();
4680       if ( type == SMDSAbs_Volume ||
4681            type < highType ||
4682            !elemSet.count(e))
4683         continue;
4684       if ( type > highType ) {
4685         nbInitElems = 0;
4686         highType    = type;
4687       }
4688       el = e;
4689       ++nbInitElems;
4690     }
4691     if ( nbInitElems == 1 ) {
4692       bool NotCreateEdge = el && el->IsMediumNode(node);
4693       if(!NotCreateEdge) {
4694         vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
4695         list<const SMDS_MeshElement*> newEdges;
4696         sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
4697       }
4698     }
4699   }
4700
4701   // Make a ceiling for each element ie an equal element of last new nodes.
4702   // Find free links of faces - make edges and sweep them into faces.
4703
4704   ElemFeatures polyFace( SMDSAbs_Face, /*isPoly=*/true ), anyFace;
4705
4706   TTElemOfElemListMap::iterator  itElem      = newElemsMap.begin();
4707   TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
4708   for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
4709   {
4710     const SMDS_MeshElement* elem = itElem->first;
4711     vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
4712
4713     if(itElem->second.size()==0) continue;
4714
4715     const bool isQuadratic = elem->IsQuadratic();
4716
4717     if ( elem->GetType() == SMDSAbs_Edge ) {
4718       // create a ceiling edge
4719       if ( !isQuadratic ) {
4720         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4721                                vecNewNodes[ 1 ]->second.back())) {
4722           myLastCreatedElems.push_back(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4723                                                       vecNewNodes[ 1 ]->second.back()));
4724           srcElements.push_back( elem );
4725         }
4726       }
4727       else {
4728         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4729                                vecNewNodes[ 1 ]->second.back(),
4730                                vecNewNodes[ 2 ]->second.back())) {
4731           myLastCreatedElems.push_back(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4732                                                       vecNewNodes[ 1 ]->second.back(),
4733                                                       vecNewNodes[ 2 ]->second.back()));
4734           srcElements.push_back( elem );
4735         }
4736       }
4737     }
4738     if ( elem->GetType() != SMDSAbs_Face )
4739       continue;
4740
4741     bool hasFreeLinks = false;
4742
4743     TIDSortedElemSet avoidSet;
4744     avoidSet.insert( elem );
4745
4746     set<const SMDS_MeshNode*> aFaceLastNodes;
4747     int iNode, nbNodes = vecNewNodes.size();
4748     if ( !isQuadratic ) {
4749       // loop on the face nodes
4750       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4751         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4752         // look for free links of the face
4753         int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
4754         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4755         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4756         // check if a link n1-n2 is free
4757         if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
4758           hasFreeLinks = true;
4759           // make a new edge and a ceiling for a new edge
4760           const SMDS_MeshElement* edge;
4761           if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
4762             myLastCreatedElems.push_back( edge = aMesh->AddEdge( n1, n2 )); // free link edge
4763             srcElements.push_back( myLastCreatedElems.back() );
4764           }
4765           n1 = vecNewNodes[ iNode ]->second.back();
4766           n2 = vecNewNodes[ iNext ]->second.back();
4767           if ( !aMesh->FindEdge( n1, n2 )) {
4768             myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2 )); // new edge ceiling
4769             srcElements.push_back( edge );
4770           }
4771         }
4772       }
4773     }
4774     else { // elem is quadratic face
4775       int nbn = nbNodes/2;
4776       for ( iNode = 0; iNode < nbn; iNode++ ) {
4777         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4778         int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
4779         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4780         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4781         const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
4782         // check if a link is free
4783         if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
4784              ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
4785              ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
4786           hasFreeLinks = true;
4787           // make an edge and a ceiling for a new edge
4788           // find medium node
4789           if ( !aMesh->FindEdge( n1, n2, n3 )) {
4790             myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2, n3 )); // free link edge
4791             srcElements.push_back( elem );
4792           }
4793           n1 = vecNewNodes[ iNode ]->second.back();
4794           n2 = vecNewNodes[ iNext ]->second.back();
4795           n3 = vecNewNodes[ iNode+nbn ]->second.back();
4796           if ( !aMesh->FindEdge( n1, n2, n3 )) {
4797             myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
4798             srcElements.push_back( elem );
4799           }
4800         }
4801       }
4802       for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
4803         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4804       }
4805     }
4806
4807     // sweep free links into faces
4808
4809     if ( hasFreeLinks ) {
4810       list<const SMDS_MeshElement*> & newVolumes = itElem->second;
4811       int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
4812
4813       set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
4814       set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
4815       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4816         initNodeSet.insert( vecNewNodes[ iNode ]->first );
4817         topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
4818       }
4819       if ( isQuadratic && nbNodes % 2 ) {  // node set for the case of a biquadratic
4820         initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
4821         initNodeSetNoCenter.erase( vecNewNodes.back()->first );
4822       }
4823       for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
4824         list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
4825         std::advance( v, volNb );
4826         // find indices of free faces of a volume and their source edges
4827         list< int > freeInd;
4828         list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
4829         SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
4830         int iF, nbF = vTool.NbFaces();
4831         for ( iF = 0; iF < nbF; iF ++ ) {
4832           if ( vTool.IsFreeFace( iF ) &&
4833                vTool.GetFaceNodes( iF, faceNodeSet ) &&
4834                initNodeSet != faceNodeSet) // except an initial face
4835           {
4836             if ( nbSteps == 1 && faceNodeSet == topNodeSet )
4837               continue;
4838             if ( faceNodeSet == initNodeSetNoCenter )
4839               continue;
4840             freeInd.push_back( iF );
4841             // find source edge of a free face iF
4842             vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
4843             vector<const SMDS_MeshNode*>::iterator lastCommom;
4844             commonNodes.resize( nbNodes, 0 );
4845             lastCommom = std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
4846                                                 initNodeSet.begin(), initNodeSet.end(),
4847                                                 commonNodes.begin());
4848             if ( std::distance( commonNodes.begin(), lastCommom ) == 3 )
4849               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
4850             else
4851               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
4852 #ifdef _DEBUG_
4853             if ( !srcEdges.back() )
4854             {
4855               cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
4856                    << iF << " of volume #" << vTool.ID() << endl;
4857             }
4858 #endif
4859           }
4860         }
4861         if ( freeInd.empty() )
4862           continue;
4863
4864         // create wall faces for all steps;
4865         // if such a face has been already created by sweep of edge,
4866         // assure that its orientation is OK
4867         for ( int iStep = 0; iStep < nbSteps; iStep++ )
4868         {
4869           vTool.Set( *v, /*ignoreCentralNodes=*/false );
4870           vTool.SetExternalNormal();
4871           const int nextShift = vTool.IsForward() ? +1 : -1;
4872           list< int >::iterator ind = freeInd.begin();
4873           list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
4874           for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
4875           {
4876             const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
4877             int nbn = vTool.NbFaceNodes( *ind );
4878             const SMDS_MeshElement * f = 0;
4879             if ( nbn == 3 )              ///// triangle
4880             {
4881               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
4882               if ( !f ||
4883                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4884               {
4885                 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
4886                                                      nodes[ 1 ],
4887                                                      nodes[ 1 + nextShift ] };
4888                 if ( f )
4889                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4890                 else
4891                   myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4892                                                                newOrder[ 2 ] ));
4893               }
4894             }
4895             else if ( nbn == 4 )       ///// quadrangle
4896             {
4897               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
4898               if ( !f ||
4899                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4900               {
4901                 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
4902                                                      nodes[ 2 ], nodes[ 2+nextShift ] };
4903                 if ( f )
4904                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4905                 else
4906                   myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4907                                                                newOrder[ 2 ], newOrder[ 3 ]));
4908               }
4909             }
4910             else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
4911             {
4912               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
4913               if ( !f ||
4914                    nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
4915               {
4916                 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
4917                                                      nodes[2],
4918                                                      nodes[2 + 2*nextShift],
4919                                                      nodes[3 - 2*nextShift],
4920                                                      nodes[3],
4921                                                      nodes[3 + 2*nextShift]};
4922                 if ( f )
4923                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4924                 else
4925                   myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ],
4926                                                                newOrder[ 1 ],
4927                                                                newOrder[ 2 ],
4928                                                                newOrder[ 3 ],
4929                                                                newOrder[ 4 ],
4930                                                                newOrder[ 5 ] ));
4931               }
4932             }
4933             else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
4934             {
4935               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
4936                                    nodes[1], nodes[3], nodes[5], nodes[7] );
4937               if ( !f ||
4938                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4939               {
4940                 const SMDS_MeshNode* newOrder[8] = { nodes[0],
4941                                                      nodes[4 - 2*nextShift],
4942                                                      nodes[4],
4943                                                      nodes[4 + 2*nextShift],
4944                                                      nodes[1],
4945                                                      nodes[5 - 2*nextShift],
4946                                                      nodes[5],
4947                                                      nodes[5 + 2*nextShift] };
4948                 if ( f )
4949                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4950                 else
4951                   myLastCreatedElems.push_back(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4952                                                               newOrder[ 2 ], newOrder[ 3 ],
4953                                                               newOrder[ 4 ], newOrder[ 5 ],
4954                                                               newOrder[ 6 ], newOrder[ 7 ]));
4955               }
4956             }
4957             else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
4958             {
4959               f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
4960                                       SMDSAbs_Face, /*noMedium=*/false);
4961               if ( !f ||
4962                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4963               {
4964                 const SMDS_MeshNode* newOrder[9] = { nodes[0],
4965                                                      nodes[4 - 2*nextShift],
4966                                                      nodes[4],
4967                                                      nodes[4 + 2*nextShift],
4968                                                      nodes[1],
4969                                                      nodes[5 - 2*nextShift],
4970                                                      nodes[5],
4971                                                      nodes[5 + 2*nextShift],
4972                                                      nodes[8] };
4973                 if ( f )
4974                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4975                 else
4976                   myLastCreatedElems.push_back(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4977                                                               newOrder[ 2 ], newOrder[ 3 ],
4978                                                               newOrder[ 4 ], newOrder[ 5 ],
4979                                                               newOrder[ 6 ], newOrder[ 7 ],
4980                                                               newOrder[ 8 ]));
4981               }
4982             }
4983             else  //////// polygon
4984             {
4985               vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
4986               const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
4987               if ( !f ||
4988                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
4989               {
4990                 if ( !vTool.IsForward() )
4991                   std::reverse( polygon_nodes.begin(), polygon_nodes.end());
4992                 if ( f )
4993                   aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
4994                 else
4995                   AddElement( polygon_nodes, polyFace.SetQuad( (*v)->IsQuadratic() ));
4996               }
4997             }
4998
4999             while ( srcElements.size() < myLastCreatedElems.size() )
5000               srcElements.push_back( *srcEdge );
5001
5002           }  // loop on free faces
5003
5004           // go to the next volume
5005           iVol = 0;
5006           while ( iVol++ < nbVolumesByStep ) v++;
5007
5008         } // loop on steps
5009       } // loop on volumes of one step
5010     } // sweep free links into faces
5011
5012     // Make a ceiling face with a normal external to a volume
5013
5014     // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
5015     SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
5016     int iF = lastVol.GetFaceIndex( aFaceLastNodes );
5017
5018     if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
5019       aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
5020       iF = lastVol.GetFaceIndex( aFaceLastNodes );
5021     }
5022     if ( iF >= 0 )
5023     {
5024       lastVol.SetExternalNormal();
5025       const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
5026       const               int nbn = lastVol.NbFaceNodes( iF );
5027       vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
5028       if ( !hasFreeLinks ||
5029            !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
5030       {
5031         const vector<int>& interlace =
5032           SMDS_MeshCell::interlacedSmdsOrder( elem->GetEntityType(), nbn );
5033         SMDS_MeshCell::applyInterlaceRev( interlace, nodeVec );
5034
5035         AddElement( nodeVec, anyFace.Init( elem ));
5036
5037         while ( srcElements.size() < myLastCreatedElems.size() )
5038           srcElements.push_back( elem );
5039       }
5040     }
5041   } // loop on swept elements
5042 }
5043
5044 //=======================================================================
5045 //function : RotationSweep
5046 //purpose  :
5047 //=======================================================================
5048
5049 SMESH_MeshEditor::PGroupIDs
5050 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet   theElemSets[2],
5051                                 const gp_Ax1&      theAxis,
5052                                 const double       theAngle,
5053                                 const int          theNbSteps,
5054                                 const double       theTol,
5055                                 const bool         theMakeGroups,
5056                                 const bool         theMakeWalls)
5057 {
5058   ClearLastCreated();
5059
5060   setElemsFirst( theElemSets );
5061   myLastCreatedElems.reserve( theElemSets[0].size() * theNbSteps );
5062   myLastCreatedNodes.reserve( theElemSets[1].size() * theNbSteps );
5063
5064   // source elements for each generated one
5065   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5066   srcElems.reserve( theElemSets[0].size() );
5067   srcNodes.reserve( theElemSets[1].size() );
5068
5069   gp_Trsf aTrsf;
5070   aTrsf.SetRotation( theAxis, theAngle );
5071   gp_Trsf aTrsf2;
5072   aTrsf2.SetRotation( theAxis, theAngle/2. );
5073
5074   gp_Lin aLine( theAxis );
5075   double aSqTol = theTol * theTol;
5076
5077   SMESHDS_Mesh* aMesh = GetMeshDS();
5078
5079   TNodeOfNodeListMap mapNewNodes;
5080   TElemOfVecOfNnlmiMap mapElemNewNodes;
5081   TTElemOfElemListMap newElemsMap;
5082
5083   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5084                                      myMesh->NbFaces(ORDER_QUADRATIC) +
5085                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
5086   // loop on theElemSets
5087   TIDSortedElemSet::iterator itElem;
5088   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5089   {
5090     TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5091     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5092       const SMDS_MeshElement* elem = *itElem;
5093       if ( !elem || elem->GetType() == SMDSAbs_Volume )
5094         continue;
5095       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5096       newNodesItVec.reserve( elem->NbNodes() );
5097
5098       // loop on elem nodes
5099       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5100       while ( itN->more() )
5101       {
5102         const SMDS_MeshNode* node = cast2Node( itN->next() );
5103
5104         gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
5105         double coord[3];
5106         aXYZ.Coord( coord[0], coord[1], coord[2] );
5107         bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
5108
5109         // check if a node has been already sweeped
5110         TNodeOfNodeListMapItr nIt =
5111           mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5112         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5113         if ( listNewNodes.empty() )
5114         {
5115           // check if we are to create medium nodes between corner ones
5116           bool needMediumNodes = false;
5117           if ( isQuadraticMesh )
5118           {
5119             SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5120             while (it->more() && !needMediumNodes )
5121             {
5122               const SMDS_MeshElement* invElem = it->next();
5123               if ( invElem != elem && !theElems.count( invElem )) continue;
5124               needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5125               if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5126                 needMediumNodes = true;
5127             }
5128           }
5129
5130           // make new nodes
5131           const SMDS_MeshNode * newNode = node;
5132           for ( int i = 0; i < theNbSteps; i++ ) {
5133             if ( !isOnAxis ) {
5134               if ( needMediumNodes )  // create a medium node
5135               {
5136                 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5137                 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5138                 myLastCreatedNodes.push_back(newNode);
5139                 srcNodes.push_back( node );
5140                 listNewNodes.push_back( newNode );
5141                 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5142               }
5143               else {
5144                 aTrsf.Transforms( coord[0], coord[1], coord[2] );
5145               }
5146               // create a corner node
5147               newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5148               myLastCreatedNodes.push_back(newNode);
5149               srcNodes.push_back( node );
5150               listNewNodes.push_back( newNode );
5151             }
5152             else {
5153               listNewNodes.push_back( newNode );
5154               // if ( needMediumNodes )
5155               //   listNewNodes.push_back( newNode );
5156             }
5157           }
5158         }
5159         newNodesItVec.push_back( nIt );
5160       }
5161       // make new elements
5162       sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
5163     }
5164   }
5165
5166   if ( theMakeWalls )
5167     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], theNbSteps, srcElems );
5168
5169   PGroupIDs newGroupIDs;
5170   if ( theMakeGroups )
5171     newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
5172
5173   return newGroupIDs;
5174 }
5175
5176 //=======================================================================
5177 //function : ExtrusParam
5178 //purpose  : standard construction
5179 //=======================================================================
5180
5181 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Vec&            theStep,
5182                                             const int                theNbSteps,
5183                                             const std::list<double>& theScales,
5184                                             const std::list<double>& theAngles,
5185                                             const gp_XYZ*            theBasePoint,
5186                                             const int                theFlags,
5187                                             const double             theTolerance):
5188   myDir( theStep ),
5189   myBaseP( Precision::Infinite(), 0, 0 ),
5190   myFlags( theFlags ),
5191   myTolerance( theTolerance ),
5192   myElemsToUse( NULL )
5193 {
5194   mySteps = new TColStd_HSequenceOfReal;
5195   const double stepSize = theStep.Magnitude();
5196   for (int i=1; i<=theNbSteps; i++ )
5197     mySteps->Append( stepSize );
5198
5199   if ( !theScales.empty() )
5200   {
5201     if ( IsScaleVariation() && (int)theScales.size() < theNbSteps )
5202       linearScaleVariation( theNbSteps, const_cast< std::list<double>& >( theScales ));
5203
5204     // add medium scales
5205     std::list<double>::const_iterator s2 = theScales.begin(), s1 = s2++;
5206     myScales.reserve( theNbSteps * 2 );
5207     myScales.push_back( 0.5 * ( *s1 + 1. ));
5208     myScales.push_back( *s1 );
5209     for ( ; s2 != theScales.end(); s1 = s2++ )
5210     {
5211       myScales.push_back( 0.5 * ( *s1 + *s2 ));
5212       myScales.push_back( *s2 );
5213     }
5214   }
5215
5216   if ( !theAngles.empty() )
5217   {
5218     std::list<double>& angles = const_cast< std::list<double>& >( theAngles );
5219     if ( IsAngleVariation() && (int)theAngles.size() < theNbSteps )
5220       linearAngleVariation( theNbSteps, angles );
5221
5222     // accumulate angles
5223     double angle = 0;
5224     int nbAngles = 0;
5225     std::list<double>::iterator a1 = angles.begin(), a2;
5226     for ( ; a1 != angles.end(); ++a1, ++nbAngles )
5227     {
5228       angle += *a1;
5229       *a1 = angle;
5230     }
5231     while ( nbAngles++ < theNbSteps )
5232       angles.push_back( angles.back() );
5233
5234     // add medium angles
5235     a2 = angles.begin(), a1 = a2++;
5236     myAngles.push_back( 0.5 * *a1 );
5237     myAngles.push_back( *a1 );
5238     for ( ; a2 != angles.end(); a1 = a2++ )
5239     {
5240       myAngles.push_back( 0.5 * ( *a1 + *a2 ));
5241       myAngles.push_back( *a2 );
5242     }
5243   }
5244
5245   if ( theBasePoint )
5246   {
5247     myBaseP = *theBasePoint;
5248   }
5249
5250   if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5251       ( theTolerance > 0 ))
5252   {
5253     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5254   }
5255   else
5256   {
5257     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5258   }
5259 }
5260
5261 //=======================================================================
5262 //function : ExtrusParam
5263 //purpose  : steps are given explicitly
5264 //=======================================================================
5265
5266 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Dir&                   theDir,
5267                                             Handle(TColStd_HSequenceOfReal) theSteps,
5268                                             const int                       theFlags,
5269                                             const double                    theTolerance):
5270   myDir( theDir ),
5271   mySteps( theSteps ),
5272   myFlags( theFlags ),
5273   myTolerance( theTolerance ),
5274   myElemsToUse( NULL )
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  : for extrusion by normal
5290 //=======================================================================
5291
5292 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const double theStepSize,
5293                                             const int    theNbSteps,
5294                                             const int    theFlags,
5295                                             const int    theDim ):
5296   myDir( 1,0,0 ),
5297   mySteps( new TColStd_HSequenceOfReal ),
5298   myFlags( theFlags ),
5299   myTolerance( 0 ),
5300   myElemsToUse( NULL )
5301 {
5302   for (int i = 0; i < theNbSteps; i++ )
5303     mySteps->Append( theStepSize );
5304
5305   if ( theDim == 1 )
5306   {
5307     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal1D;
5308   }
5309   else
5310   {
5311     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal2D;
5312   }
5313 }
5314
5315 //=======================================================================
5316 //function : ExtrusParam
5317 //purpose  : for extrusion along path
5318 //=======================================================================
5319
5320 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const std::vector< PathPoint >& thePoints,
5321                                             const gp_Pnt*                   theBasePoint,
5322                                             const std::list<double>&        theScales,
5323                                             const bool                      theMakeGroups )
5324   : myBaseP( Precision::Infinite(), 0, 0 ),
5325     myFlags( EXTRUSION_FLAG_BOUNDARY | ( theMakeGroups ? EXTRUSION_FLAG_GROUPS : 0 )),
5326     myPathPoints( thePoints )
5327 {
5328   if ( theBasePoint )
5329   {
5330     myBaseP = theBasePoint->XYZ();
5331   }
5332
5333   if ( !theScales.empty() )
5334   {
5335     // add medium scales
5336     std::list<double>::const_iterator s2 = theScales.begin(), s1 = s2++;
5337     myScales.reserve( thePoints.size() * 2 );
5338     myScales.push_back( 0.5 * ( 1. + *s1 ));
5339     myScales.push_back( *s1 );
5340     for ( ; s2 != theScales.end(); s1 = s2++ )
5341     {
5342       myScales.push_back( 0.5 * ( *s1 + *s2 ));
5343       myScales.push_back( *s2 );
5344     }
5345   }
5346
5347   myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesAlongTrack;
5348 }
5349
5350 //=======================================================================
5351 //function : ExtrusParam::SetElementsToUse
5352 //purpose  : stores elements to use for extrusion by normal, depending on
5353 //           state of EXTRUSION_FLAG_USE_INPUT_ELEMS_ONLY flag;
5354 //           define myBaseP for scaling
5355 //=======================================================================
5356
5357 void SMESH_MeshEditor::ExtrusParam::SetElementsToUse( const TIDSortedElemSet& elems,
5358                                                       const TIDSortedElemSet& nodes )
5359 {
5360   myElemsToUse = ToUseInpElemsOnly() ? & elems : 0;
5361
5362   if ( Precision::IsInfinite( myBaseP.X() )) // myBaseP not defined
5363   {
5364     myBaseP.SetCoord( 0.,0.,0. );
5365     TIDSortedElemSet newNodes;
5366
5367     const TIDSortedElemSet* elemSets[] = { &elems, &nodes };
5368     for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5369     {
5370       const TIDSortedElemSet& elements = *( elemSets[ is2ndSet ]);
5371       TIDSortedElemSet::const_iterator itElem = elements.begin();
5372       for ( ; itElem != elements.end(); itElem++ )
5373       {
5374         const SMDS_MeshElement* elem = *itElem;
5375         SMDS_ElemIteratorPtr     itN = elem->nodesIterator();
5376         while ( itN->more() ) {
5377           const SMDS_MeshElement* node = itN->next();
5378           if ( newNodes.insert( node ).second )
5379             myBaseP += SMESH_NodeXYZ( node );
5380         }
5381       }
5382     }
5383     myBaseP /= newNodes.size();
5384   }
5385 }
5386
5387 //=======================================================================
5388 //function : ExtrusParam::beginStepIter
5389 //purpose  : prepare iteration on steps
5390 //=======================================================================
5391
5392 void SMESH_MeshEditor::ExtrusParam::beginStepIter( bool withMediumNodes )
5393 {
5394   myWithMediumNodes = withMediumNodes;
5395   myNextStep = 1;
5396   myCurSteps.clear();
5397 }
5398 //=======================================================================
5399 //function : ExtrusParam::moreSteps
5400 //purpose  : are there more steps?
5401 //=======================================================================
5402
5403 bool SMESH_MeshEditor::ExtrusParam::moreSteps()
5404 {
5405   return myNextStep <= mySteps->Length() || !myCurSteps.empty();
5406 }
5407 //=======================================================================
5408 //function : ExtrusParam::nextStep
5409 //purpose  : returns the next step
5410 //=======================================================================
5411
5412 double SMESH_MeshEditor::ExtrusParam::nextStep()
5413 {
5414   double res = 0;
5415   if ( !myCurSteps.empty() )
5416   {
5417     res = myCurSteps.back();
5418     myCurSteps.pop_back();
5419   }
5420   else if ( myNextStep <= mySteps->Length() )
5421   {
5422     myCurSteps.push_back( mySteps->Value( myNextStep ));
5423     ++myNextStep;
5424     if ( myWithMediumNodes )
5425     {
5426       myCurSteps.back() /= 2.;
5427       myCurSteps.push_back( myCurSteps.back() );
5428     }
5429     res = nextStep();
5430   }
5431   return res;
5432 }
5433
5434 //=======================================================================
5435 //function : ExtrusParam::makeNodesByDir
5436 //purpose  : create nodes for standard extrusion
5437 //=======================================================================
5438
5439 int SMESH_MeshEditor::ExtrusParam::
5440 makeNodesByDir( SMESHDS_Mesh*                     mesh,
5441                 const SMDS_MeshNode*              srcNode,
5442                 std::list<const SMDS_MeshNode*> & newNodes,
5443                 const bool                        makeMediumNodes)
5444 {
5445   gp_XYZ p = SMESH_NodeXYZ( srcNode );
5446
5447   int nbNodes = 0;
5448   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5449   {
5450     p += myDir.XYZ() * nextStep();
5451     const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5452     newNodes.push_back( newNode );
5453   }
5454
5455   if ( !myScales.empty() || !myAngles.empty() )
5456   {
5457     gp_XYZ  center = myBaseP;
5458     gp_Ax1  ratationAxis( center, myDir );
5459     gp_Trsf rotation;
5460
5461     std::list<const SMDS_MeshNode*>::iterator nIt = newNodes.begin();
5462     size_t i = !makeMediumNodes;
5463     for ( beginStepIter( makeMediumNodes );
5464           moreSteps();
5465           ++nIt, i += 1 + !makeMediumNodes )
5466     {
5467       center += myDir.XYZ() * nextStep();
5468
5469       gp_XYZ xyz = SMESH_NodeXYZ( *nIt );
5470       bool moved = false;
5471       if ( i < myScales.size() )
5472       {
5473         xyz = ( myScales[i] * ( xyz - center )) + center;
5474         moved = true;
5475       }
5476       if ( !myAngles.empty() )
5477       {
5478         rotation.SetRotation( ratationAxis, myAngles[i] );
5479         rotation.Transforms( xyz );
5480         moved = true;
5481       }
5482       if ( moved )
5483         mesh->MoveNode( *nIt, xyz.X(), xyz.Y(), xyz.Z() );
5484       else
5485         break;
5486     }
5487   }
5488   return nbNodes;
5489 }
5490
5491 //=======================================================================
5492 //function : ExtrusParam::makeNodesByDirAndSew
5493 //purpose  : create nodes for standard extrusion with sewing
5494 //=======================================================================
5495
5496 int SMESH_MeshEditor::ExtrusParam::
5497 makeNodesByDirAndSew( SMESHDS_Mesh*                     mesh,
5498                       const SMDS_MeshNode*              srcNode,
5499                       std::list<const SMDS_MeshNode*> & newNodes,
5500                       const bool                        makeMediumNodes)
5501 {
5502   gp_XYZ P1 = SMESH_NodeXYZ( srcNode );
5503
5504   int nbNodes = 0;
5505   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5506   {
5507     P1 += myDir.XYZ() * nextStep();
5508
5509     // try to search in sequence of existing nodes
5510     // if myNodes.size()>0 we 'nave to use given sequence
5511     // else - use all nodes of mesh
5512     const SMDS_MeshNode * node = 0;
5513     if ( myNodes.Length() > 0 )
5514     {
5515       for ( int i = 1; i <= myNodes.Length(); i++ )
5516       {
5517         SMESH_NodeXYZ P2 = myNodes.Value(i);
5518         if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5519         {
5520           node = myNodes.Value(i);
5521           break;
5522         }
5523       }
5524     }
5525     else
5526     {
5527       SMDS_NodeIteratorPtr itn = mesh->nodesIterator();
5528       while(itn->more())
5529       {
5530         SMESH_NodeXYZ P2 = itn->next();
5531         if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5532         {
5533           node = P2._node;
5534           break;
5535         }
5536       }
5537     }
5538
5539     if ( !node )
5540       node = mesh->AddNode( P1.X(), P1.Y(), P1.Z() );
5541
5542     newNodes.push_back( node );
5543
5544   } // loop on steps
5545
5546   return nbNodes;
5547 }
5548
5549 //=======================================================================
5550 //function : ExtrusParam::makeNodesByNormal2D
5551 //purpose  : create nodes for extrusion using normals of faces
5552 //=======================================================================
5553
5554 int SMESH_MeshEditor::ExtrusParam::
5555 makeNodesByNormal2D( SMESHDS_Mesh*                     mesh,
5556                      const SMDS_MeshNode*              srcNode,
5557                      std::list<const SMDS_MeshNode*> & newNodes,
5558                      const bool                        makeMediumNodes)
5559 {
5560   const bool alongAvgNorm = ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL );
5561
5562   gp_XYZ p = SMESH_NodeXYZ( srcNode );
5563
5564   // get normals to faces sharing srcNode
5565   vector< gp_XYZ > norms, baryCenters;
5566   gp_XYZ norm, avgNorm( 0,0,0 );
5567   SMDS_ElemIteratorPtr faceIt = srcNode->GetInverseElementIterator( SMDSAbs_Face );
5568   while ( faceIt->more() )
5569   {
5570     const SMDS_MeshElement* face = faceIt->next();
5571     if ( myElemsToUse && !myElemsToUse->count( face ))
5572       continue;
5573     if ( SMESH_MeshAlgos::FaceNormal( face, norm, /*normalized=*/true ))
5574     {
5575       norms.push_back( norm );
5576       avgNorm += norm;
5577       if ( !alongAvgNorm )
5578       {
5579         gp_XYZ bc(0,0,0);
5580         int nbN = 0;
5581         for ( SMDS_ElemIteratorPtr nIt = face->nodesIterator(); nIt->more(); ++nbN )
5582           bc += SMESH_NodeXYZ( nIt->next() );
5583         baryCenters.push_back( bc / nbN );
5584       }
5585     }
5586   }
5587
5588   if ( norms.empty() ) return 0;
5589
5590   double normSize = avgNorm.Modulus();
5591   if ( normSize < std::numeric_limits<double>::min() )
5592     return 0;
5593
5594   if ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL ) // extrude along avgNorm
5595   {
5596     myDir = avgNorm;
5597     return makeNodesByDir( mesh, srcNode, newNodes, makeMediumNodes );
5598   }
5599
5600   avgNorm /= normSize;
5601
5602   int nbNodes = 0;
5603   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5604   {
5605     gp_XYZ pNew = p;
5606     double stepSize = nextStep();
5607
5608     if ( norms.size() > 1 )
5609     {
5610       for ( size_t iF = 0; iF < norms.size(); ++iF ) // loop on faces
5611       {
5612         // translate plane of a face
5613         baryCenters[ iF ] += norms[ iF ] * stepSize;
5614
5615         // find point of intersection of the face plane located at baryCenters[ iF ]
5616         // and avgNorm located at pNew
5617         double d    = -( norms[ iF ] * baryCenters[ iF ]); // d of plane equation ax+by+cz+d=0
5618         double dot  = ( norms[ iF ] * avgNorm );
5619         if ( dot < std::numeric_limits<double>::min() )
5620           dot = stepSize * 1e-3;
5621         double step = -( norms[ iF ] * pNew + d ) / dot;
5622         pNew += step * avgNorm;
5623       }
5624     }
5625     else
5626     {
5627       pNew += stepSize * avgNorm;
5628     }
5629     p = pNew;
5630
5631     const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5632     newNodes.push_back( newNode );
5633   }
5634   return nbNodes;
5635 }
5636
5637 //=======================================================================
5638 //function : ExtrusParam::makeNodesByNormal1D
5639 //purpose  : create nodes for extrusion using normals of edges
5640 //=======================================================================
5641
5642 int SMESH_MeshEditor::ExtrusParam::
5643 makeNodesByNormal1D( SMESHDS_Mesh*                     mesh,
5644                      const SMDS_MeshNode*              srcNode,
5645                      std::list<const SMDS_MeshNode*> & newNodes,
5646                      const bool                        makeMediumNodes)
5647 {
5648   throw SALOME_Exception("Extrusion 1D by Normal not implemented");
5649   return 0;
5650 }
5651
5652 //=======================================================================
5653 //function : ExtrusParam::makeNodesAlongTrack
5654 //purpose  : create nodes for extrusion along path
5655 //=======================================================================
5656
5657 int SMESH_MeshEditor::ExtrusParam::
5658 makeNodesAlongTrack( SMESHDS_Mesh*                     mesh,
5659                      const SMDS_MeshNode*              srcNode,
5660                      std::list<const SMDS_MeshNode*> & newNodes,
5661                      const bool                        makeMediumNodes)
5662 {
5663   const Standard_Real aTolAng=1.e-4;
5664
5665   gp_Pnt aV0x = myBaseP;
5666   gp_Pnt aPN0 = SMESH_NodeXYZ( srcNode );
5667
5668   const PathPoint& aPP0 = myPathPoints[0];
5669   gp_Pnt aP0x = aPP0.myPnt;
5670   gp_Dir aDT0x= aPP0.myTgt;
5671
5672   std::vector< gp_Pnt > centers;
5673   centers.reserve( NbSteps() * 2 );
5674
5675   gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
5676
5677   for ( size_t j = 1; j < myPathPoints.size(); ++j )
5678   {
5679     const PathPoint&  aPP  = myPathPoints[j];
5680     const gp_Pnt&     aP1x = aPP.myPnt;
5681     const gp_Dir&    aDT1x = aPP.myTgt;
5682
5683     // Translation
5684     gp_Vec aV01x( aP0x, aP1x );
5685     aTrsf.SetTranslation( aV01x );
5686     gp_Pnt aV1x = aV0x.Transformed( aTrsf );
5687     gp_Pnt aPN1 = aPN0.Transformed( aTrsf );
5688
5689     // rotation 1 [ T1,T0 ]
5690     Standard_Real aAngleT1T0 = -aDT1x.Angle( aDT0x );
5691     if ( fabs( aAngleT1T0 ) > aTolAng )
5692     {
5693       gp_Dir aDT1T0 = aDT1x ^ aDT0x;
5694       aTrsfRotT1T0.SetRotation( gp_Ax1( aV1x, aDT1T0 ), aAngleT1T0 );
5695
5696       aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
5697     }
5698
5699     // rotation 2
5700     if ( aPP.myAngle != 0. )
5701     {
5702       aTrsfRot.SetRotation( gp_Ax1( aV1x, aDT1x ), aPP.myAngle );
5703       aPN1 = aPN1.Transformed( aTrsfRot );
5704     }
5705
5706     // make new node
5707     if ( makeMediumNodes )
5708     {
5709       // create additional node
5710       gp_XYZ midP = 0.5 * ( aPN1.XYZ() + aPN0.XYZ() );
5711       const SMDS_MeshNode* newNode = mesh->AddNode( midP.X(), midP.Y(), midP.Z() );
5712       newNodes.push_back( newNode );
5713
5714     }
5715     const SMDS_MeshNode* newNode = mesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
5716     newNodes.push_back( newNode );
5717
5718     centers.push_back( 0.5 * ( aV0x.XYZ() + aV1x.XYZ() ));
5719     centers.push_back( aV1x );
5720
5721     aPN0 = aPN1;
5722     aP0x = aP1x;
5723     aV0x = aV1x;
5724     aDT0x = aDT1x;
5725   }
5726
5727   // scale
5728   if ( !myScales.empty() )
5729   {
5730     gp_Trsf aTrsfScale;
5731     std::list<const SMDS_MeshNode*>::iterator node = newNodes.begin();
5732     for ( size_t i = !makeMediumNodes;
5733           i < myScales.size() && node != newNodes.end();
5734           i += ( 1 + !makeMediumNodes ), ++node )
5735     {
5736       aTrsfScale.SetScale( centers[ i ], myScales[ i ] );
5737       gp_Pnt aN = SMESH_NodeXYZ( *node );
5738       gp_Pnt aP = aN.Transformed( aTrsfScale );
5739       mesh->MoveNode( *node, aP.X(), aP.Y(), aP.Z() );
5740     }
5741   }
5742
5743   return myPathPoints.size() + makeMediumNodes * ( myPathPoints.size() - 2 );
5744 }
5745
5746 //=======================================================================
5747 //function : ExtrusionSweep
5748 //purpose  :
5749 //=======================================================================
5750
5751 SMESH_MeshEditor::PGroupIDs
5752 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet     theElems[2],
5753                                   const gp_Vec&        theStep,
5754                                   const int            theNbSteps,
5755                                   TTElemOfElemListMap& newElemsMap,
5756                                   const int            theFlags,
5757                                   const double         theTolerance)
5758 {
5759   std::list<double> dummy;
5760   ExtrusParam aParams( theStep, theNbSteps, dummy, dummy, 0,
5761                        theFlags, theTolerance );
5762   return ExtrusionSweep( theElems, aParams, newElemsMap );
5763 }
5764
5765 namespace
5766 {
5767
5768 //=======================================================================
5769 //function : getOriFactor
5770 //purpose  : Return -1 or 1 depending on if order of given nodes corresponds to
5771 //           edge curve orientation
5772 //=======================================================================
5773
5774   double getOriFactor( const TopoDS_Edge&   edge,
5775                        const SMDS_MeshNode* n1,
5776                        const SMDS_MeshNode* n2,
5777                        SMESH_MesherHelper&  helper)
5778   {
5779     double u1 = helper.GetNodeU( edge, n1, n2 );
5780     double u2 = helper.GetNodeU( edge, n2, n1 );
5781     return u1 < u2 ? 1. : -1.;
5782   }
5783 }
5784
5785 //=======================================================================
5786 //function : ExtrusionSweep
5787 //purpose  :
5788 //=======================================================================
5789
5790 SMESH_MeshEditor::PGroupIDs
5791 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet     theElemSets[2],
5792                                   ExtrusParam&         theParams,
5793                                   TTElemOfElemListMap& newElemsMap)
5794 {
5795   ClearLastCreated();
5796
5797   setElemsFirst( theElemSets );
5798   myLastCreatedElems.reserve( theElemSets[0].size() * theParams.NbSteps() );
5799   myLastCreatedNodes.reserve( theElemSets[1].size() * theParams.NbSteps() );
5800
5801   // source elements for each generated one
5802   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5803   srcElems.reserve( theElemSets[0].size() );
5804   srcNodes.reserve( theElemSets[1].size() );
5805
5806   const int nbSteps = theParams.NbSteps();
5807   theParams.SetElementsToUse( theElemSets[0], theElemSets[1] );
5808
5809   TNodeOfNodeListMap   mapNewNodes;
5810   TElemOfVecOfNnlmiMap mapElemNewNodes;
5811
5812   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5813                                      myMesh->NbFaces(ORDER_QUADRATIC) +
5814                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
5815   // loop on theElems
5816   TIDSortedElemSet::iterator itElem;
5817   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5818   {
5819     TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5820     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5821     {
5822       // check element type
5823       const SMDS_MeshElement* elem = *itElem;
5824       if ( !elem  || elem->GetType() == SMDSAbs_Volume )
5825         continue;
5826
5827       const size_t nbNodes = elem->NbNodes();
5828       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5829       newNodesItVec.reserve( nbNodes );
5830
5831       // loop on elem nodes
5832       SMDS_NodeIteratorPtr itN = elem->nodeIterator();
5833       while ( itN->more() )
5834       {
5835         // check if a node has been already sweeped
5836         const SMDS_MeshNode* node = itN->next();
5837         TNodeOfNodeListMap::iterator nIt =
5838           mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5839         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5840         if ( listNewNodes.empty() )
5841         {
5842           // make new nodes
5843
5844           // check if we are to create medium nodes between corner ones
5845           bool needMediumNodes = false;
5846           if ( isQuadraticMesh )
5847           {
5848             SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5849             while (it->more() && !needMediumNodes )
5850             {
5851               const SMDS_MeshElement* invElem = it->next();
5852               if ( invElem != elem && !theElems.count( invElem )) continue;
5853               needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5854               if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5855                 needMediumNodes = true;
5856             }
5857           }
5858           // create nodes for all steps
5859           if ( theParams.MakeNodes( GetMeshDS(), node, listNewNodes, needMediumNodes ))
5860           {
5861             list<const SMDS_MeshNode*>::iterator newNodesIt = listNewNodes.begin();
5862             for ( ; newNodesIt != listNewNodes.end(); ++newNodesIt )
5863             {
5864               myLastCreatedNodes.push_back( *newNodesIt );
5865               srcNodes.push_back( node );
5866             }
5867           }
5868           else
5869           {
5870             if ( theParams.ToMakeBoundary() )
5871             {
5872               GetMeshDS()->Modified();
5873               throw SALOME_Exception( SMESH_Comment("Can't extrude node #") << node->GetID() );
5874             }
5875             break; // newNodesItVec will be shorter than nbNodes
5876           }
5877         }
5878         newNodesItVec.push_back( nIt );
5879       }
5880       // make new elements
5881       if ( newNodesItVec.size() == nbNodes )
5882         sweepElement( elem, newNodesItVec, newElemsMap[elem], nbSteps, srcElems );
5883     }
5884   }
5885
5886   if ( theParams.ToMakeBoundary() ) {
5887     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], nbSteps, srcElems );
5888   }
5889   PGroupIDs newGroupIDs;
5890   if ( theParams.ToMakeGroups() )
5891     newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
5892
5893   return newGroupIDs;
5894 }
5895
5896 //=======================================================================
5897 //function : ExtrusionAlongTrack
5898 //purpose  :
5899 //=======================================================================
5900 SMESH_MeshEditor::Extrusion_Error
5901 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet     theElements[2],
5902                                        SMESH_Mesh*          theTrackMesh,
5903                                        SMDS_ElemIteratorPtr theTrackIterator,
5904                                        const SMDS_MeshNode* theN1,
5905                                        std::list<double>&   theAngles,
5906                                        const bool           theAngleVariation,
5907                                        std::list<double>&   theScales,
5908                                        const bool           theScaleVariation,
5909                                        const gp_Pnt*        theRefPoint,
5910                                        const bool           theMakeGroups)
5911 {
5912   ClearLastCreated();
5913
5914   // 1. Check data
5915   if ( theElements[0].empty() && theElements[1].empty() )
5916     return EXTR_NO_ELEMENTS;
5917
5918   ASSERT( theTrackMesh );
5919   if ( ! theTrackIterator || !theTrackIterator->more() )
5920     return EXTR_NO_ELEMENTS;
5921
5922   // 2. Get ordered nodes
5923   SMESH_MeshAlgos::TElemGroupVector branchEdges;
5924   SMESH_MeshAlgos::TNodeGroupVector branchNods;
5925   SMESH_MeshAlgos::Get1DBranches( theTrackIterator, branchEdges, branchNods, theN1 );
5926   if ( branchEdges.empty() )
5927     return EXTR_PATH_NOT_EDGE;
5928
5929   if ( branchEdges.size() > 1 )
5930     return EXTR_BAD_PATH_SHAPE;
5931
5932   std::vector< const SMDS_MeshNode* >&    pathNodes = branchNods[0];
5933   std::vector< const SMDS_MeshElement* >& pathEdges = branchEdges[0];
5934   if ( pathNodes[0] != theN1 && pathNodes[1] != theN1 )
5935     return EXTR_BAD_STARTING_NODE;
5936
5937   if ( theTrackMesh->NbEdges( ORDER_QUADRATIC ) > 0 )
5938   {
5939     // add medium nodes to pathNodes
5940     std::vector< const SMDS_MeshNode* >    pathNodes2;
5941     std::vector< const SMDS_MeshElement* > pathEdges2;
5942     pathNodes2.reserve( pathNodes.size() * 2 );
5943     pathEdges2.reserve( pathEdges.size() * 2 );
5944     for ( size_t i = 0; i < pathEdges.size(); ++i )
5945     {
5946       pathNodes2.push_back( pathNodes[i] );
5947       pathEdges2.push_back( pathEdges[i] );
5948       if ( pathEdges[i]->IsQuadratic() )
5949       {
5950         pathNodes2.push_back( pathEdges[i]->GetNode(2) );
5951         pathEdges2.push_back( pathEdges[i] );
5952       }
5953     }
5954     pathNodes2.push_back( pathNodes.back() );
5955     pathEdges.swap( pathEdges2 );
5956     pathNodes.swap( pathNodes2 );
5957   }
5958
5959   // 3. Get path data at pathNodes
5960
5961   std::vector< ExtrusParam::PathPoint > points( pathNodes.size() );
5962
5963   if ( theAngleVariation )
5964     linearAngleVariation( points.size()-1, theAngles );
5965   if ( theScaleVariation )
5966     linearScaleVariation( points.size()-1, theScales );
5967
5968   theAngles.push_front( 0 ); // for the 1st point that is not transformed
5969   std::list<double>::iterator angle = theAngles.begin();
5970
5971   SMESHDS_Mesh* pathMeshDS = theTrackMesh->GetMeshDS();
5972
5973   std::map< int, double > edgeID2OriFactor; // orientation of EDGEs
5974   std::map< int, double >::iterator id2factor;
5975   SMESH_MesherHelper pathHelper( *theTrackMesh );
5976   gp_Pnt p; gp_Vec tangent;
5977   const double tol2 = gp::Resolution() * gp::Resolution();
5978
5979   for ( size_t i = 0; i < pathNodes.size(); ++i )
5980   {
5981     ExtrusParam::PathPoint & point = points[ i ];
5982
5983     point.myPnt = SMESH_NodeXYZ( pathNodes[ i ]);
5984
5985     if ( angle != theAngles.end() )
5986       point.myAngle = *angle++;
5987
5988     tangent.SetCoord( 0,0,0 );
5989     const int          shapeID = pathNodes[ i ]->GetShapeID();
5990     const TopoDS_Shape&  shape = pathMeshDS->IndexToShape( shapeID );
5991     TopAbs_ShapeEnum shapeType = shape.IsNull() ? TopAbs_SHAPE : shape.ShapeType();
5992     switch ( shapeType )
5993     {
5994     case TopAbs_EDGE:
5995     {
5996       TopoDS_Edge edge = TopoDS::Edge( shape );
5997       id2factor = edgeID2OriFactor.insert( std::make_pair( shapeID, 0 )).first;
5998       if ( id2factor->second == 0 )
5999       {
6000         if ( i ) id2factor->second = getOriFactor( edge, pathNodes[i-1], pathNodes[i], pathHelper );
6001         else     id2factor->second = getOriFactor( edge, pathNodes[i], pathNodes[i+1], pathHelper );
6002       }
6003       double u = pathHelper.GetNodeU( edge, pathNodes[i] ), u0, u1;
6004       Handle(Geom_Curve) curve = BRep_Tool::Curve( edge, u0, u1 );
6005       curve->D1( u, p, tangent );
6006       tangent *= id2factor->second;
6007       break;
6008     }
6009     case TopAbs_VERTEX:
6010     {
6011       int nbEdges = 0;
6012       PShapeIteratorPtr shapeIt = pathHelper.GetAncestors( shape, *theTrackMesh, TopAbs_EDGE );
6013       while ( const TopoDS_Shape* edgePtr = shapeIt->next() )
6014       {
6015         int edgeID = pathMeshDS->ShapeToIndex( *edgePtr );
6016         for ( int di = -1; di <= 0; ++di )
6017         {
6018           size_t j = i + di;
6019           if ( j < pathEdges.size() && edgeID == pathEdges[ j ]->GetShapeID() )
6020           {
6021             TopoDS_Edge edge = TopoDS::Edge( *edgePtr );
6022             id2factor = edgeID2OriFactor.insert( std::make_pair( edgeID, 0 )).first;
6023             if ( id2factor->second == 0 )
6024             {
6025               if ( j < i )
6026                 id2factor->second = getOriFactor( edge, pathNodes[i-1], pathNodes[i], pathHelper );
6027               else
6028                 id2factor->second = getOriFactor( edge, pathNodes[i], pathNodes[i+1], pathHelper );
6029             }
6030             double u = pathHelper.GetNodeU( edge, pathNodes[i] ), u0, u1;
6031             Handle(Geom_Curve) curve = BRep_Tool::Curve( edge, u0, u1 );
6032             gp_Vec du;
6033             curve->D1( u, p, du );
6034             double size2 = du.SquareMagnitude();
6035             if ( du.SquareMagnitude() > tol2 )
6036             {
6037               tangent += du.Divided( Sqrt( size2 )) * id2factor->second;
6038               nbEdges++;
6039             }
6040             break;
6041           }
6042         }
6043       }
6044       if ( nbEdges > 0 )
6045         break;
6046     }
6047     default:
6048     {
6049       for ( int di = -1; di <= 1; di += 2 )
6050       {
6051         size_t j = i + di;
6052         if ( j < pathNodes.size() )
6053         {
6054           gp_Vec dir( point.myPnt, SMESH_NodeXYZ( pathNodes[ j ]));
6055           double size2 = dir.SquareMagnitude();
6056           if ( size2 > tol2 )
6057             tangent += dir.Divided( Sqrt( size2 )) * di;
6058         }
6059       }
6060     }
6061     } // switch ( shapeType )
6062
6063     if ( tangent.SquareMagnitude() < tol2 )
6064       return EXTR_CANT_GET_TANGENT;
6065
6066     point.myTgt = tangent;
6067
6068   } // loop on pathNodes
6069
6070
6071   ExtrusParam nodeMaker( points, theRefPoint, theScales, theMakeGroups );
6072   TTElemOfElemListMap newElemsMap;
6073
6074   ExtrusionSweep( theElements, nodeMaker, newElemsMap );
6075
6076   return EXTR_OK;
6077 }
6078
6079 //=======================================================================
6080 //function : linearAngleVariation
6081 //purpose  : spread values over nbSteps
6082 //=======================================================================
6083
6084 void SMESH_MeshEditor::linearAngleVariation(const int     nbSteps,
6085                                             list<double>& Angles)
6086 {
6087   int nbAngles = Angles.size();
6088   if( nbSteps > nbAngles && nbAngles > 0 )
6089   {
6090     vector<double> theAngles(nbAngles);
6091     theAngles.assign( Angles.begin(), Angles.end() );
6092
6093     list<double> res;
6094     double rAn2St = double( nbAngles ) / double( nbSteps );
6095     double angPrev = 0, angle;
6096     for ( int iSt = 0; iSt < nbSteps; ++iSt )
6097     {
6098       double angCur = rAn2St * ( iSt+1 );
6099       double angCurFloor  = floor( angCur );
6100       double angPrevFloor = floor( angPrev );
6101       if ( angPrevFloor == angCurFloor )
6102         angle = rAn2St * theAngles[ int( angCurFloor ) ];
6103       else {
6104         int iP = int( angPrevFloor );
6105         double angPrevCeil = ceil(angPrev);
6106         angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6107
6108         int iC = int( angCurFloor );
6109         if ( iC < nbAngles )
6110           angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6111
6112         iP = int( angPrevCeil );
6113         while ( iC-- > iP )
6114           angle += theAngles[ iC ];
6115       }
6116       res.push_back(angle);
6117       angPrev = angCur;
6118     }
6119     Angles.swap( res );
6120   }
6121 }
6122
6123 //=======================================================================
6124 //function : linearScaleVariation
6125 //purpose  : spread values over nbSteps 
6126 //=======================================================================
6127
6128 void SMESH_MeshEditor::linearScaleVariation(const int          theNbSteps,
6129                                             std::list<double>& theScales)
6130 {
6131   int nbScales = theScales.size();
6132   std::vector<double> myScales;
6133   myScales.reserve( theNbSteps );
6134   std::list<double>::const_iterator scale = theScales.begin();
6135   double prevScale = 1.0;
6136   for ( int iSc = 1; scale != theScales.end(); ++scale, ++iSc )
6137   {
6138     int      iStep = int( iSc / double( nbScales ) * theNbSteps + 0.5 );
6139     int    stDelta = Max( 1, iStep - myScales.size());
6140     double scDelta = ( *scale - prevScale ) / stDelta;
6141     for ( int iStep = 0; iStep < stDelta; ++iStep )
6142     {
6143       myScales.push_back( prevScale + scDelta );
6144       prevScale = myScales.back();
6145     }
6146     prevScale = *scale;
6147   }
6148   theScales.assign( myScales.begin(), myScales.end() );
6149 }
6150
6151 //================================================================================
6152 /*!
6153  * \brief Move or copy theElements applying theTrsf to their nodes
6154  *  \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6155  *  \param theTrsf - transformation to apply
6156  *  \param theCopy - if true, create translated copies of theElems
6157  *  \param theMakeGroups - if true and theCopy, create translated groups
6158  *  \param theTargetMesh - mesh to copy translated elements into
6159  *  \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6160  */
6161 //================================================================================
6162
6163 SMESH_MeshEditor::PGroupIDs
6164 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6165                              const gp_Trsf&     theTrsf,
6166                              const bool         theCopy,
6167                              const bool         theMakeGroups,
6168                              SMESH_Mesh*        theTargetMesh)
6169 {
6170   ClearLastCreated();
6171   myLastCreatedElems.reserve( theElems.size() );
6172
6173   bool needReverse = false;
6174   string groupPostfix;
6175   switch ( theTrsf.Form() ) {
6176   case gp_PntMirror:
6177     needReverse = true;
6178     groupPostfix = "mirrored";
6179     break;
6180   case gp_Ax1Mirror:
6181     groupPostfix = "mirrored";
6182     break;
6183   case gp_Ax2Mirror:
6184     needReverse = true;
6185     groupPostfix = "mirrored";
6186     break;
6187   case gp_Rotation:
6188     groupPostfix = "rotated";
6189     break;
6190   case gp_Translation:
6191     groupPostfix = "translated";
6192     break;
6193   case gp_Scale:
6194     groupPostfix = "scaled";
6195     break;
6196   case gp_CompoundTrsf: // different scale by axis
6197     groupPostfix = "scaled";
6198     break;
6199   default:
6200     needReverse = false;
6201     groupPostfix = "transformed";
6202   }
6203
6204   SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6205   SMESHDS_Mesh* aMesh    = GetMeshDS();
6206
6207   SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6208   SMESH_MeshEditor* editor = theTargetMesh ? & targetMeshEditor : theCopy ? this : 0;
6209   SMESH_MeshEditor::ElemFeatures elemType;
6210
6211   // map old node to new one
6212   TNodeNodeMap nodeMap;
6213
6214   // elements sharing moved nodes; those of them which have all
6215   // nodes mirrored but are not in theElems are to be reversed
6216   TIDSortedElemSet inverseElemSet;
6217
6218   // source elements for each generated one
6219   SMESH_SequenceOfElemPtr srcElems, srcNodes;
6220
6221   // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6222   TIDSortedElemSet orphanNode;
6223
6224   if ( theElems.empty() ) // transform the whole mesh
6225   {
6226     // add all elements
6227     SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6228     while ( eIt->more() ) theElems.insert( eIt->next() );
6229     // add orphan nodes
6230     SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6231     while ( nIt->more() )
6232     {
6233       const SMDS_MeshNode* node = nIt->next();
6234       if ( node->NbInverseElements() == 0)
6235         orphanNode.insert( node );
6236     }
6237   }
6238
6239   // loop on elements to transform nodes : first orphan nodes then elems
6240   TIDSortedElemSet::iterator itElem;
6241   TIDSortedElemSet *elements[] = { &orphanNode, &theElems };
6242   for (int i=0; i<2; i++)
6243     for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ )
6244     {
6245       const SMDS_MeshElement* elem = *itElem;
6246       if ( !elem )
6247         continue;
6248
6249       // loop on elem nodes
6250       double coord[3];
6251       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6252       while ( itN->more() )
6253       {
6254         const SMDS_MeshNode* node = cast2Node( itN->next() );
6255         // check if a node has been already transformed
6256         pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6257           nodeMap.insert( make_pair ( node, node ));
6258         if ( !n2n_isnew.second )
6259           continue;
6260
6261         node->GetXYZ( coord );
6262         theTrsf.Transforms( coord[0], coord[1], coord[2] );
6263         if ( theTargetMesh ) {
6264           const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6265           n2n_isnew.first->second = newNode;
6266           myLastCreatedNodes.push_back(newNode);
6267           srcNodes.push_back( node );
6268         }
6269         else if ( theCopy ) {
6270           const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6271           n2n_isnew.first->second = newNode;
6272           myLastCreatedNodes.push_back(newNode);
6273           srcNodes.push_back( node );
6274         }
6275         else {
6276           aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6277           // node position on shape becomes invalid
6278           const_cast< SMDS_MeshNode* > ( node )->SetPosition
6279             ( SMDS_SpacePosition::originSpacePosition() );
6280         }
6281
6282         // keep inverse elements
6283         if ( !theCopy && !theTargetMesh && needReverse ) {
6284           SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6285           while ( invElemIt->more() ) {
6286             const SMDS_MeshElement* iel = invElemIt->next();
6287             inverseElemSet.insert( iel );
6288           }
6289         }
6290       }
6291     } // loop on elems in { &orphanNode, &theElems };
6292
6293   // either create new elements or reverse mirrored ones
6294   if ( !theCopy && !needReverse && !theTargetMesh )
6295     return PGroupIDs();
6296
6297   theElems.insert( inverseElemSet.begin(),inverseElemSet.end() );
6298
6299   // Replicate or reverse elements
6300
6301   std::vector<int> iForw;
6302   vector<const SMDS_MeshNode*> nodes;
6303   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6304   {
6305     const SMDS_MeshElement* elem = *itElem;
6306     if ( !elem ) continue;
6307
6308     SMDSAbs_GeometryType geomType = elem->GetGeomType();
6309     size_t               nbNodes  = elem->NbNodes();
6310     if ( geomType == SMDSGeom_NONE ) continue; // node
6311
6312     nodes.resize( nbNodes );
6313
6314     if ( geomType == SMDSGeom_POLYHEDRA )  // ------------------ polyhedral volume
6315     {
6316       const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( elem );
6317       if ( !aPolyedre )
6318         continue;
6319       nodes.clear();
6320       bool allTransformed = true;
6321       int nbFaces = aPolyedre->NbFaces();
6322       for (int iface = 1; iface <= nbFaces && allTransformed; iface++)
6323       {
6324         int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6325         for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++)
6326         {
6327           const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6328           TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6329           if ( nodeMapIt == nodeMap.end() )
6330             allTransformed = false; // not all nodes transformed
6331           else
6332             nodes.push_back((*nodeMapIt).second);
6333         }
6334         if ( needReverse && allTransformed )
6335           std::reverse( nodes.end() - nbFaceNodes, nodes.end() );
6336       }
6337       if ( !allTransformed )
6338         continue; // not all nodes transformed
6339     }
6340     else // ----------------------- the rest element types
6341     {
6342       while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6343       const vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType(), nbNodes );
6344       const vector<int>&    i = needReverse ? iRev : iForw;
6345
6346       // find transformed nodes
6347       size_t iNode = 0;
6348       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6349       while ( itN->more() ) {
6350         const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
6351         TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6352         if ( nodeMapIt == nodeMap.end() )
6353           break; // not all nodes transformed
6354         nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
6355       }
6356       if ( iNode != nbNodes )
6357         continue; // not all nodes transformed
6358     }
6359
6360     if ( editor ) {
6361       // copy in this or a new mesh
6362       if ( editor->AddElement( nodes, elemType.Init( elem, /*basicOnly=*/false )))
6363         srcElems.push_back( elem );
6364     }
6365     else {
6366       // reverse element as it was reversed by transformation
6367       if ( nbNodes > 2 )
6368         aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
6369     }
6370
6371   } // loop on elements
6372
6373   if ( editor && editor != this )
6374     myLastCreatedElems.swap( editor->myLastCreatedElems );
6375
6376   PGroupIDs newGroupIDs;
6377
6378   if ( ( theMakeGroups && theCopy ) ||
6379        ( theMakeGroups && theTargetMesh ) )
6380     newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh, false );
6381
6382   return newGroupIDs;
6383 }
6384
6385 //================================================================================
6386 /*!
6387  * \brief Make an offset mesh from a source 2D mesh
6388  *  \param [in] theElements - source faces
6389  *  \param [in] theValue - offset value
6390  *  \param [out] theTgtMesh - a mesh to add offset elements to
6391  *  \param [in] theMakeGroups - to generate groups
6392  *  \return PGroupIDs - IDs of created groups. NULL means failure
6393  */
6394 //================================================================================
6395
6396 SMESH_MeshEditor::PGroupIDs SMESH_MeshEditor::Offset( TIDSortedElemSet & theElements,
6397                                                       const double       theValue,
6398                                                       SMESH_Mesh*        theTgtMesh,
6399                                                       const bool         theMakeGroups,
6400                                                       const bool         theCopyElements,
6401                                                       const bool         theFixSelfIntersection)
6402 {
6403   SMESHDS_Mesh*    meshDS = GetMeshDS();
6404   SMESHDS_Mesh* tgtMeshDS = theTgtMesh->GetMeshDS();
6405   SMESH_MeshEditor tgtEditor( theTgtMesh );
6406
6407   SMDS_ElemIteratorPtr eIt;
6408   if ( theElements.empty() ) eIt = meshDS->elementsIterator( SMDSAbs_Face );
6409   else                       eIt = SMESHUtils::elemSetIterator( theElements );
6410
6411   SMESH_MeshAlgos::TElemIntPairVec new2OldFaces;
6412   SMESH_MeshAlgos::TNodeIntPairVec new2OldNodes;
6413   std::unique_ptr< SMDS_Mesh > offsetMesh
6414     ( SMESH_MeshAlgos::MakeOffset( eIt, *meshDS, theValue,
6415                                    theFixSelfIntersection,
6416                                    new2OldFaces, new2OldNodes ));
6417   if ( offsetMesh->NbElements() == 0 )
6418     return PGroupIDs(); // MakeOffset() failed
6419
6420
6421   if ( theTgtMesh == myMesh && !theCopyElements )
6422   {
6423     // clear the source elements
6424     if ( theElements.empty() ) eIt = meshDS->elementsIterator( SMDSAbs_Face );
6425     else                       eIt = SMESHUtils::elemSetIterator( theElements );
6426     while ( eIt->more() )
6427       meshDS->RemoveFreeElement( eIt->next(), 0 );
6428   }
6429
6430   // offsetMesh->Modified();
6431   // offsetMesh->CompactMesh(); // make IDs start from 1
6432
6433   // source elements for each generated one
6434   SMESH_SequenceOfElemPtr srcElems, srcNodes;
6435   srcElems.reserve( new2OldFaces.size() );
6436   srcNodes.reserve( new2OldNodes.size() );
6437
6438   ClearLastCreated();
6439   myLastCreatedElems.reserve( new2OldFaces.size() );
6440   myLastCreatedNodes.reserve( new2OldNodes.size() );
6441
6442   // copy offsetMesh to theTgtMesh
6443
6444   int idShift = meshDS->MaxNodeID();
6445   for ( size_t i = 0; i < new2OldNodes.size(); ++i )
6446     if ( const SMDS_MeshNode* n = new2OldNodes[ i ].first )
6447     {
6448 #ifndef _DEBUG_
6449       if ( n->NbInverseElements() > 0 )
6450 #endif
6451       {
6452         const SMDS_MeshNode* n2 =
6453           tgtMeshDS->AddNodeWithID( n->X(), n->Y(), n->Z(), idShift + n->GetID() );
6454         myLastCreatedNodes.push_back( n2 );
6455         srcNodes.push_back( meshDS->FindNode( new2OldNodes[ i ].second ));
6456       }
6457     }
6458
6459   ElemFeatures elemType;
6460   for ( size_t i = 0; i < new2OldFaces.size(); ++i )
6461     if ( const SMDS_MeshElement* f = new2OldFaces[ i ].first )
6462     {
6463       elemType.Init( f );
6464       elemType.myNodes.clear();
6465       for ( SMDS_NodeIteratorPtr nIt = f->nodeIterator(); nIt->more(); )
6466       {
6467         const SMDS_MeshNode* n2 = nIt->next();
6468         elemType.myNodes.push_back( tgtMeshDS->FindNode( idShift + n2->GetID() ));
6469       }
6470       tgtEditor.AddElement( elemType.myNodes, elemType );
6471       srcElems.push_back( meshDS->FindElement( new2OldFaces[ i ].second ));
6472     }
6473
6474   myLastCreatedElems.swap( tgtEditor.myLastCreatedElems );
6475
6476   PGroupIDs newGroupIDs;
6477   if ( theMakeGroups )
6478     newGroupIDs = generateGroups( srcNodes, srcElems, "offset", theTgtMesh, false );
6479   else
6480     newGroupIDs.reset( new std::list< int > );
6481
6482   return newGroupIDs;
6483 }
6484
6485 //=======================================================================
6486 /*!
6487  * \brief Create groups of elements made during transformation
6488  *  \param nodeGens - nodes making corresponding myLastCreatedNodes
6489  *  \param elemGens - elements making corresponding myLastCreatedElems
6490  *  \param postfix - to push_back to names of new groups
6491  *  \param targetMesh - mesh to create groups in
6492  *  \param topPresent - is there are "top" elements that are created by sweeping
6493  */
6494 //=======================================================================
6495
6496 SMESH_MeshEditor::PGroupIDs
6497 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
6498                                  const SMESH_SequenceOfElemPtr& elemGens,
6499                                  const std::string&             postfix,
6500                                  SMESH_Mesh*                    targetMesh,
6501                                  const bool                     topPresent)
6502 {
6503   PGroupIDs newGroupIDs( new list<int> );
6504   SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6505
6506   // Sort existing groups by types and collect their names
6507
6508   // containers to store an old group and generated new ones;
6509   // 1st new group is for result elems of different type than a source one;
6510   // 2nd new group is for same type result elems ("top" group at extrusion)
6511   using boost::tuple;
6512   using boost::make_tuple;
6513   typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
6514   vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
6515   vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
6516   // group names
6517   set< string > groupNames;
6518
6519   SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
6520   if ( !groupIt->more() ) return newGroupIDs;
6521
6522   int newGroupID = mesh->GetGroupIds().back()+1;
6523   while ( groupIt->more() )
6524   {
6525     SMESH_Group * group = groupIt->next();
6526     if ( !group ) continue;
6527     SMESHDS_GroupBase* groupDS = group->GetGroupDS();
6528     if ( !groupDS || groupDS->IsEmpty() ) continue;
6529     groupNames.insert    ( group->GetName() );
6530     groupDS->SetStoreName( group->GetName() );
6531     const SMDSAbs_ElementType type = groupDS->GetType();
6532     SMESHDS_Group* newGroup    = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6533     SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6534     groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
6535     orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
6536   }
6537
6538   // Loop on nodes and elements to add them in new groups
6539
6540   vector< const SMDS_MeshElement* > resultElems;
6541   for ( int isNodes = 0; isNodes < 2; ++isNodes )
6542   {
6543     const SMESH_SequenceOfElemPtr& gens  = isNodes ? nodeGens : elemGens;
6544     const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
6545     if ( gens.size() != elems.size() )
6546       throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
6547
6548     // loop on created elements
6549     for (size_t iElem = 0; iElem < elems.size(); ++iElem )
6550     {
6551       const SMDS_MeshElement* sourceElem = gens[ iElem ];
6552       if ( !sourceElem ) {
6553         MESSAGE("generateGroups(): NULL source element");
6554         continue;
6555       }
6556       list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
6557       if ( groupsOldNew.empty() ) { // no groups of this type at all
6558         while ( iElem+1 < gens.size() && gens[ iElem+1 ] == sourceElem )
6559           ++iElem; // skip all elements made by sourceElem
6560         continue;
6561       }
6562       // collect all elements made by the iElem-th sourceElem
6563       resultElems.clear();
6564       if ( const SMDS_MeshElement* resElem = elems[ iElem ])
6565         if ( resElem != sourceElem )
6566           resultElems.push_back( resElem );
6567       while ( iElem+1 < gens.size() && gens[ iElem+1 ] == sourceElem )
6568         if ( const SMDS_MeshElement* resElem = elems[ ++iElem ])
6569           if ( resElem != sourceElem )
6570             resultElems.push_back( resElem );
6571
6572       const SMDS_MeshElement* topElem = 0;
6573       if ( isNodes ) // there must be a top element
6574       {
6575         topElem = resultElems.back();
6576         resultElems.pop_back();
6577       }
6578       else
6579       {
6580         vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
6581         for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
6582           if ( (*resElemIt)->GetType() == sourceElem->GetType() )
6583           {
6584             topElem = *resElemIt;
6585             *resElemIt = 0; // erase *resElemIt
6586             break;
6587           }
6588       }
6589       // add resultElems to groups originted from ones the sourceElem belongs to
6590       list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
6591       for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
6592       {
6593         SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
6594         if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
6595         {
6596           // fill in a new group
6597           SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
6598           vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
6599           for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
6600             if ( *resElemIt )
6601               newGroup.Add( *resElemIt );
6602
6603           // fill a "top" group
6604           if ( topElem )
6605           {
6606             SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
6607             newTopGroup.Add( topElem );
6608           }
6609         }
6610       }
6611     } // loop on created elements
6612   }// loop on nodes and elements
6613
6614   // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
6615
6616   list<int> topGrouIds;
6617   for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
6618   {
6619     SMESHDS_GroupBase* oldGroupDS =   orderedOldNewGroups[i]->get<0>();
6620     SMESHDS_Group*   newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
6621                                       orderedOldNewGroups[i]->get<2>() };
6622     for ( int is2nd = 0; is2nd < 2; ++is2nd )
6623     {
6624       SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
6625       if ( newGroupDS->IsEmpty() )
6626       {
6627         mesh->GetMeshDS()->RemoveGroup( newGroupDS );
6628       }
6629       else
6630       {
6631         // set group type
6632         newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
6633
6634         // make a name
6635         const bool isTop = ( topPresent &&
6636                              newGroupDS->GetType() == oldGroupDS->GetType() &&
6637                              is2nd );
6638
6639         string name = oldGroupDS->GetStoreName();
6640         { // remove trailing whitespaces (issue 22599)
6641           size_t size = name.size();
6642           while ( size > 1 && isspace( name[ size-1 ]))
6643             --size;
6644           if ( size != name.size() )
6645           {
6646             name.resize( size );
6647             oldGroupDS->SetStoreName( name.c_str() );
6648           }
6649         }
6650         if ( !targetMesh ) {
6651           string suffix = ( isTop ? "top": postfix.c_str() );
6652           name += "_";
6653           name += suffix;
6654           int nb = 1;
6655           while ( !groupNames.insert( name ).second ) // name exists
6656             name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
6657         }
6658         else if ( isTop ) {
6659           name += "_top";
6660         }
6661         newGroupDS->SetStoreName( name.c_str() );
6662
6663         // make a SMESH_Groups
6664         mesh->AddGroup( newGroupDS );
6665         if ( isTop )
6666           topGrouIds.push_back( newGroupDS->GetID() );
6667         else
6668           newGroupIDs->push_back( newGroupDS->GetID() );
6669       }
6670     }
6671   }
6672   newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
6673
6674   return newGroupIDs;
6675 }
6676
6677 //================================================================================
6678 /*!
6679  *  * \brief Return list of group of nodes close to each other within theTolerance
6680  *  *        Search among theNodes or in the whole mesh if theNodes is empty using
6681  *  *        an Octree algorithm
6682  *  \param [in,out] theNodes - the nodes to treat
6683  *  \param [in]     theTolerance - the tolerance
6684  *  \param [out]    theGroupsOfNodes - the result groups of coincident nodes
6685  *  \param [in]     theSeparateCornersAndMedium - if \c true, in quadratic mesh puts 
6686  *         corner and medium nodes in separate groups
6687  */
6688 //================================================================================
6689
6690 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet &   theNodes,
6691                                             const double         theTolerance,
6692                                             TListOfListOfNodes & theGroupsOfNodes,
6693                                             bool                 theSeparateCornersAndMedium)
6694 {
6695   ClearLastCreated();
6696
6697   if ( myMesh->NbEdges  ( ORDER_QUADRATIC ) +
6698        myMesh->NbFaces  ( ORDER_QUADRATIC ) +
6699        myMesh->NbVolumes( ORDER_QUADRATIC ) == 0 )
6700     theSeparateCornersAndMedium = false;
6701
6702   TIDSortedNodeSet& corners = theNodes;
6703   TIDSortedNodeSet  medium;
6704
6705   if ( theNodes.empty() ) // get all nodes in the mesh
6706   {
6707     TIDSortedNodeSet* nodes[2] = { &corners, &medium };
6708     SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator();
6709     if ( theSeparateCornersAndMedium )
6710       while ( nIt->more() )
6711       {
6712         const SMDS_MeshNode* n = nIt->next();
6713         TIDSortedNodeSet* & nodeSet = nodes[ SMESH_MesherHelper::IsMedium( n )];
6714         nodeSet->insert( nodeSet->end(), n );
6715       }
6716     else
6717       while ( nIt->more() )
6718         theNodes.insert( theNodes.end(), nIt->next() );
6719   }
6720   else if ( theSeparateCornersAndMedium ) // separate corners from medium nodes
6721   {
6722     TIDSortedNodeSet::iterator nIt = corners.begin();
6723     while ( nIt != corners.end() )
6724       if ( SMESH_MesherHelper::IsMedium( *nIt ))
6725       {
6726         medium.insert( medium.end(), *nIt );
6727         corners.erase( nIt++ );
6728       }
6729       else
6730       {
6731         ++nIt;
6732       }
6733   }
6734
6735   if ( !corners.empty() )
6736     SMESH_OctreeNode::FindCoincidentNodes ( corners, &theGroupsOfNodes, theTolerance );
6737   if ( !medium.empty() )
6738     SMESH_OctreeNode::FindCoincidentNodes ( medium, &theGroupsOfNodes, theTolerance );
6739 }
6740
6741 //=======================================================================
6742 //function : SimplifyFace
6743 //purpose  : split a chain of nodes into several closed chains
6744 //=======================================================================
6745
6746 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
6747                                     vector<const SMDS_MeshNode *>&       poly_nodes,
6748                                     vector<int>&                         quantities) const
6749 {
6750   int nbNodes = faceNodes.size();
6751   while ( faceNodes[ 0 ] == faceNodes[ nbNodes-1 ] && nbNodes > 2 )
6752     --nbNodes;
6753   if ( nbNodes < 3 )
6754     return 0;
6755   size_t prevNbQuant = quantities.size();
6756
6757   vector< const SMDS_MeshNode* > simpleNodes; simpleNodes.reserve( nbNodes );
6758   map< const SMDS_MeshNode*, int > nodeIndices; // indices within simpleNodes
6759   map< const SMDS_MeshNode*, int >::iterator nInd;
6760
6761   nodeIndices.insert( make_pair( faceNodes[0], 0 ));
6762   simpleNodes.push_back( faceNodes[0] );
6763   for ( int iCur = 1; iCur < nbNodes; iCur++ )
6764   {
6765     if ( faceNodes[ iCur ] != simpleNodes.back() )
6766     {
6767       int index = simpleNodes.size();
6768       nInd = nodeIndices.insert( make_pair( faceNodes[ iCur ], index )).first;
6769       int prevIndex = nInd->second;
6770       if ( prevIndex < index )
6771       {
6772         // a sub-loop found
6773         int loopLen = index - prevIndex;
6774         if ( loopLen > 2 )
6775         {
6776           // store the sub-loop
6777           quantities.push_back( loopLen );
6778           for ( int i = prevIndex; i < index; i++ )
6779             poly_nodes.push_back( simpleNodes[ i ]);
6780         }
6781         simpleNodes.resize( prevIndex+1 );
6782       }
6783       else
6784       {
6785         simpleNodes.push_back( faceNodes[ iCur ]);
6786       }
6787     }
6788   }
6789
6790   if ( simpleNodes.size() > 2 )
6791   {
6792     quantities.push_back( simpleNodes.size() );
6793     poly_nodes.insert ( poly_nodes.end(), simpleNodes.begin(), simpleNodes.end() );
6794   }
6795
6796   return quantities.size() - prevNbQuant;
6797 }
6798
6799 //=======================================================================
6800 //function : MergeNodes
6801 //purpose  : In each group, the cdr of nodes are substituted by the first one
6802 //           in all elements.
6803 //=======================================================================
6804
6805 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes,
6806                                    const bool           theAvoidMakingHoles)
6807 {
6808   ClearLastCreated();
6809
6810   SMESHDS_Mesh* mesh = GetMeshDS();
6811
6812   TNodeNodeMap nodeNodeMap; // node to replace - new node
6813   set<const SMDS_MeshElement*> elems; // all elements with changed nodes
6814   list< int > rmElemIds, rmNodeIds;
6815   vector< ElemFeatures > newElemDefs;
6816
6817   // Fill nodeNodeMap and elems
6818
6819   TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
6820   for ( ; grIt != theGroupsOfNodes.end(); grIt++ )
6821   {
6822     list<const SMDS_MeshNode*>& nodes = *grIt;
6823     list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
6824     const SMDS_MeshNode* nToKeep = *nIt;
6825     for ( ++nIt; nIt != nodes.end(); nIt++ )
6826     {
6827       const SMDS_MeshNode* nToRemove = *nIt;
6828       nodeNodeMap.insert( make_pair( nToRemove, nToKeep ));
6829       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
6830       while ( invElemIt->more() ) {
6831         const SMDS_MeshElement* elem = invElemIt->next();
6832         elems.insert(elem);
6833       }
6834     }
6835   }
6836
6837   // Apply recursive replacements (BUG 0020185)
6838   TNodeNodeMap::iterator nnIt = nodeNodeMap.begin();
6839   for ( ; nnIt != nodeNodeMap.end(); ++nnIt )
6840   {
6841     const SMDS_MeshNode* nToKeep = nnIt->second;
6842     TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( nToKeep );
6843     while ( nnIt_i != nodeNodeMap.end() && nnIt_i->second != nnIt->second )
6844     {
6845       nToKeep = nnIt_i->second;
6846       nnIt->second = nToKeep;
6847       nnIt_i = nodeNodeMap.find( nToKeep );
6848     }
6849   }
6850
6851   if ( theAvoidMakingHoles )
6852   {
6853     // find elements whose topology changes
6854
6855     vector<const SMDS_MeshElement*> pbElems;
6856     set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
6857     for ( ; eIt != elems.end(); ++eIt )
6858     {
6859       const SMDS_MeshElement* elem = *eIt;
6860       SMDS_ElemIteratorPtr     itN = elem->nodesIterator();
6861       while ( itN->more() )
6862       {
6863         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
6864         TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
6865         if ( nnIt != nodeNodeMap.end() && elem->GetNodeIndex( nnIt->second ) >= 0 )
6866         {
6867           // several nodes of elem stick
6868           pbElems.push_back( elem );
6869           break;
6870         }
6871       }
6872     }
6873     // exclude from merge nodes causing spoiling element
6874     for ( size_t iLoop = 0; iLoop < pbElems.size(); ++iLoop ) // avoid infinite cycle
6875     {
6876       bool nodesExcluded = false;
6877       for ( size_t i = 0; i < pbElems.size(); ++i )
6878       {
6879         size_t prevNbMergeNodes = nodeNodeMap.size();
6880         if ( !applyMerge( pbElems[i], newElemDefs, nodeNodeMap, /*noHoles=*/true ) &&
6881              prevNbMergeNodes < nodeNodeMap.size() )
6882           nodesExcluded = true;
6883       }
6884       if ( !nodesExcluded )
6885         break;
6886     }
6887   }
6888
6889   for ( nnIt = nodeNodeMap.begin(); nnIt != nodeNodeMap.end(); ++nnIt )
6890   {
6891     const SMDS_MeshNode* nToRemove = nnIt->first;
6892     const SMDS_MeshNode* nToKeep   = nnIt->second;
6893     if ( nToRemove != nToKeep )
6894     {
6895       rmNodeIds.push_back( nToRemove->GetID() );
6896       AddToSameGroups( nToKeep, nToRemove, mesh );
6897       // set _alwaysComputed to a sub-mesh of VERTEX to enable further mesh computing
6898       // w/o creating node in place of merged ones.
6899       SMDS_PositionPtr pos = nToRemove->GetPosition();
6900       if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
6901         if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
6902           sm->SetIsAlwaysComputed( true );
6903     }
6904   }
6905
6906   // Change element nodes or remove an element
6907
6908   set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
6909   for ( ; eIt != elems.end(); eIt++ )
6910   {
6911     const SMDS_MeshElement* elem = *eIt;
6912     SMESHDS_SubMesh*          sm = mesh->MeshElements( elem->getshapeId() );
6913
6914     bool keepElem = applyMerge( elem, newElemDefs, nodeNodeMap, /*noHoles=*/false );
6915     if ( !keepElem )
6916       rmElemIds.push_back( elem->GetID() );
6917
6918     for ( size_t i = 0; i < newElemDefs.size(); ++i )
6919     {
6920       if ( i > 0 || !mesh->ChangeElementNodes( elem,
6921                                                & newElemDefs[i].myNodes[0],
6922                                                newElemDefs[i].myNodes.size() ))
6923       {
6924         if ( i == 0 )
6925         {
6926           newElemDefs[i].SetID( elem->GetID() );
6927           mesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
6928           if ( !keepElem ) rmElemIds.pop_back();
6929         }
6930         else
6931         {
6932           newElemDefs[i].SetID( -1 );
6933         }
6934         SMDS_MeshElement* newElem = this->AddElement( newElemDefs[i].myNodes, newElemDefs[i] );
6935         if ( sm && newElem )
6936           sm->AddElement( newElem );
6937         if ( elem != newElem )
6938           ReplaceElemInGroups( elem, newElem, mesh );
6939       }
6940     }
6941   }
6942
6943   // Remove bad elements, then equal nodes (order important)
6944   Remove( rmElemIds, /*isNodes=*/false );
6945   Remove( rmNodeIds, /*isNodes=*/true );
6946
6947   return;
6948 }
6949
6950 //=======================================================================
6951 //function : applyMerge
6952 //purpose  : Compute new connectivity of an element after merging nodes
6953 //  \param [in] elems - the element
6954 //  \param [out] newElemDefs - definition(s) of result element(s)
6955 //  \param [inout] nodeNodeMap - nodes to merge
6956 //  \param [in] avoidMakingHoles - if true and and the element becomes invalid
6957 //              after merging (but not degenerated), removes nodes causing
6958 //              the invalidity from \a nodeNodeMap.
6959 //  \return bool - true if the element should be removed
6960 //=======================================================================
6961
6962 bool SMESH_MeshEditor::applyMerge( const SMDS_MeshElement* elem,
6963                                    vector< ElemFeatures >& newElemDefs,
6964                                    TNodeNodeMap&           nodeNodeMap,
6965                                    const bool              avoidMakingHoles )
6966 {
6967   bool toRemove = false; // to remove elem
6968   int nbResElems = 1;    // nb new elements
6969
6970   newElemDefs.resize(nbResElems);
6971   newElemDefs[0].Init( elem );
6972   newElemDefs[0].myNodes.clear();
6973
6974   set<const SMDS_MeshNode*> nodeSet;
6975   vector< const SMDS_MeshNode*>   curNodes;
6976   vector< const SMDS_MeshNode*> & uniqueNodes = newElemDefs[0].myNodes;
6977   vector<int> iRepl;
6978
6979   const        int  nbNodes = elem->NbNodes();
6980   SMDSAbs_EntityType entity = elem->GetEntityType();
6981
6982   curNodes.resize( nbNodes );
6983   uniqueNodes.resize( nbNodes );
6984   iRepl.resize( nbNodes );
6985   int iUnique = 0, iCur = 0, nbRepl = 0;
6986
6987   // Get new seq of nodes
6988
6989   SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6990   while ( itN->more() )
6991   {
6992     const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
6993
6994     TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
6995     if ( nnIt != nodeNodeMap.end() ) {
6996       n = (*nnIt).second;
6997     }
6998     curNodes[ iCur ] = n;
6999     bool isUnique = nodeSet.insert( n ).second;
7000     if ( isUnique )
7001       uniqueNodes[ iUnique++ ] = n;
7002     else
7003       iRepl[ nbRepl++ ] = iCur;
7004     iCur++;
7005   }
7006
7007   // Analyse element topology after replacement
7008
7009   int nbUniqueNodes = nodeSet.size();
7010   if ( nbNodes != nbUniqueNodes ) // some nodes stick
7011   {
7012     toRemove = true;
7013     nbResElems = 0;
7014
7015     if ( newElemDefs[0].myIsQuad && newElemDefs[0].myType == SMDSAbs_Face && nbNodes > 6 )
7016     {
7017       // if corner nodes stick, remove medium nodes between them from uniqueNodes
7018       int nbCorners = nbNodes / 2;
7019       for ( int iCur = 0; iCur < nbCorners; ++iCur )
7020       {
7021         int iNext = ( iCur + 1 ) % nbCorners;
7022         if ( curNodes[ iCur ] == curNodes[ iNext ] ) // corners stick
7023         {
7024           int iMedium = iCur + nbCorners;
7025           vector< const SMDS_MeshNode* >::iterator i =
7026             std::find( uniqueNodes.begin() + nbCorners - nbRepl,
7027                        uniqueNodes.end(),
7028                        curNodes[ iMedium ]);
7029           if ( i != uniqueNodes.end() )
7030           {
7031             --nbUniqueNodes;
7032             for ( ; i+1 != uniqueNodes.end(); ++i )
7033               *i = *(i+1);
7034           }
7035         }
7036       }
7037     }
7038
7039     switch ( entity )
7040     {
7041     case SMDSEntity_Polygon:
7042     case SMDSEntity_Quad_Polygon: // Polygon
7043     {
7044       ElemFeatures* elemType = & newElemDefs[0];
7045       const bool isQuad = elemType->myIsQuad;
7046       if ( isQuad )
7047         SMDS_MeshCell::applyInterlace // interlace medium and corner nodes
7048           ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon, nbNodes ), curNodes );
7049
7050       // a polygon can divide into several elements
7051       vector<const SMDS_MeshNode *> polygons_nodes;
7052       vector<int> quantities;
7053       nbResElems = SimplifyFace( curNodes, polygons_nodes, quantities );
7054       newElemDefs.resize( nbResElems );
7055       for ( int inode = 0, iface = 0; iface < nbResElems; iface++ )
7056       {
7057         ElemFeatures* elemType = & newElemDefs[iface];
7058         if ( iface ) elemType->Init( elem );
7059
7060         vector<const SMDS_MeshNode *>& face_nodes = elemType->myNodes;
7061         int nbNewNodes = quantities[iface];
7062         face_nodes.assign( polygons_nodes.begin() + inode,
7063                            polygons_nodes.begin() + inode + nbNewNodes );
7064         inode += nbNewNodes;
7065         if ( isQuad ) // check if a result elem is a valid quadratic polygon
7066         {
7067           bool isValid = ( nbNewNodes % 2 == 0 );
7068           for ( int i = 0; i < nbNewNodes && isValid; ++i )
7069             isValid = ( elem->IsMediumNode( face_nodes[i]) == bool( i % 2 ));
7070           elemType->SetQuad( isValid );
7071           if ( isValid ) // put medium nodes after corners
7072             SMDS_MeshCell::applyInterlaceRev
7073               ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon,
7074                                                     nbNewNodes ), face_nodes );
7075         }
7076         elemType->SetPoly(( nbNewNodes / ( elemType->myIsQuad + 1 ) > 4 ));
7077       }
7078       nbUniqueNodes = newElemDefs[0].myNodes.size();
7079       break;
7080     } // Polygon
7081
7082     case SMDSEntity_Polyhedra: // Polyhedral volume
7083     {
7084       if ( nbUniqueNodes >= 4 )
7085       {
7086         // each face has to be analyzed in order to check volume validity
7087         if ( const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( elem ))
7088         {
7089           int nbFaces = aPolyedre->NbFaces();
7090
7091           vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7092           vector<int>                  & quantities = newElemDefs[0].myPolyhedQuantities;
7093           vector<const SMDS_MeshNode *>  faceNodes;
7094           poly_nodes.clear();
7095           quantities.clear();
7096
7097           for (int iface = 1; iface <= nbFaces; iface++)
7098           {
7099             int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7100             faceNodes.resize( nbFaceNodes );
7101             for (int inode = 1; inode <= nbFaceNodes; inode++)
7102             {
7103               const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7104               TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7105               if ( nnIt != nodeNodeMap.end() ) // faceNode sticks
7106                 faceNode = (*nnIt).second;
7107               faceNodes[inode - 1] = faceNode;
7108             }
7109             SimplifyFace(faceNodes, poly_nodes, quantities);
7110           }
7111
7112           if ( quantities.size() > 3 )
7113           {
7114             // TODO: remove coincident faces
7115             nbResElems = 1;
7116             nbUniqueNodes = newElemDefs[0].myNodes.size();
7117           }
7118         }
7119       }
7120     }
7121     break;
7122
7123     // Regular elements
7124     // TODO not all the possible cases are solved. Find something more generic?
7125     case SMDSEntity_Edge: //////// EDGE
7126     case SMDSEntity_Triangle: //// TRIANGLE
7127     case SMDSEntity_Quad_Triangle:
7128     case SMDSEntity_Tetra:
7129     case SMDSEntity_Quad_Tetra: // TETRAHEDRON
7130     {
7131       break;
7132     }
7133     case SMDSEntity_Quad_Edge:
7134     {
7135       break;
7136     }
7137     case SMDSEntity_Quadrangle: //////////////////////////////////// QUADRANGLE
7138     {
7139       if ( nbUniqueNodes < 3 )
7140         toRemove = true;
7141       else if ( nbRepl == 1 && curNodes[ iRepl[0]] == curNodes[( iRepl[0]+2 )%4 ])
7142         toRemove = true; // opposite nodes stick
7143       else
7144         toRemove = false;
7145       break;
7146     }
7147     case SMDSEntity_Quad_Quadrangle: // Quadratic QUADRANGLE
7148     {
7149       //   1    5    2
7150       //    +---+---+
7151       //    |       |
7152       //   4+       +6
7153       //    |       |
7154       //    +---+---+
7155       //   0    7    3
7156       if ( nbUniqueNodes == 6 &&
7157            iRepl[0] < 4       &&
7158            ( nbRepl == 1 || iRepl[1] >= 4 ))
7159       {
7160         toRemove = false;
7161       }
7162       break;
7163     }
7164     case SMDSEntity_BiQuad_Quadrangle: // Bi-Quadratic QUADRANGLE
7165     {
7166       //   1    5    2
7167       //    +---+---+
7168       //    |       |
7169       //   4+  8+   +6
7170       //    |       |
7171       //    +---+---+
7172       //   0    7    3
7173       if ( nbUniqueNodes == 7 &&
7174            iRepl[0] < 4       &&
7175            ( nbRepl == 1 || iRepl[1] != 8 ))
7176       {
7177         toRemove = false;
7178       }
7179       break;
7180     }
7181     case SMDSEntity_Penta: ///////////////////////////////////// PENTAHEDRON
7182     {
7183       if ( nbUniqueNodes == 4 ) {
7184         // ---------------------------------> tetrahedron
7185         if ( curNodes[3] == curNodes[4] &&
7186              curNodes[3] == curNodes[5] ) {
7187           // top nodes stick
7188           toRemove = false;
7189         }
7190         else if ( curNodes[0] == curNodes[1] &&
7191                   curNodes[0] == curNodes[2] ) {
7192           // bottom nodes stick: set a top before
7193           uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7194           uniqueNodes[ 0 ] = curNodes [ 5 ];
7195           uniqueNodes[ 1 ] = curNodes [ 4 ];
7196           uniqueNodes[ 2 ] = curNodes [ 3 ];
7197           toRemove = false;
7198         }
7199         else if (( curNodes[0] == curNodes[3] ) +
7200                  ( curNodes[1] == curNodes[4] ) +
7201                  ( curNodes[2] == curNodes[5] ) == 2 ) {
7202           // a lateral face turns into a line
7203           toRemove = false;
7204         }
7205       }
7206       else if ( nbUniqueNodes == 5 ) {
7207         // PENTAHEDRON --------------------> pyramid
7208         if ( curNodes[0] == curNodes[3] )
7209         {
7210           uniqueNodes[ 0 ] = curNodes[ 1 ];
7211           uniqueNodes[ 1 ] = curNodes[ 4 ];
7212           uniqueNodes[ 2 ] = curNodes[ 5 ];
7213           uniqueNodes[ 3 ] = curNodes[ 2 ];
7214           uniqueNodes[ 4 ] = curNodes[ 0 ];
7215           toRemove = false;
7216         }
7217         if ( curNodes[1] == curNodes[4] )
7218         {
7219           uniqueNodes[ 0 ] = curNodes[ 0 ];
7220           uniqueNodes[ 1 ] = curNodes[ 2 ];
7221           uniqueNodes[ 2 ] = curNodes[ 5 ];
7222           uniqueNodes[ 3 ] = curNodes[ 3 ];
7223           uniqueNodes[ 4 ] = curNodes[ 1 ];
7224           toRemove = false;
7225         }
7226         if ( curNodes[2] == curNodes[5] )
7227         {
7228           uniqueNodes[ 0 ] = curNodes[ 0 ];
7229           uniqueNodes[ 1 ] = curNodes[ 3 ];
7230           uniqueNodes[ 2 ] = curNodes[ 4 ];
7231           uniqueNodes[ 3 ] = curNodes[ 1 ];
7232           uniqueNodes[ 4 ] = curNodes[ 2 ];
7233           toRemove = false;
7234         }
7235       }
7236       break;
7237     }
7238     case SMDSEntity_Hexa:
7239     {
7240       //////////////////////////////////// HEXAHEDRON
7241       SMDS_VolumeTool hexa (elem);
7242       hexa.SetExternalNormal();
7243       if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7244         //////////////////////// HEX ---> tetrahedron
7245         for ( int iFace = 0; iFace < 6; iFace++ ) {
7246           const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7247           if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7248               curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7249               curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7250             // one face turns into a point ...
7251             int  pickInd = ind[ 0 ];
7252             int iOppFace = hexa.GetOppFaceIndex( iFace );
7253             ind = hexa.GetFaceNodesIndices( iOppFace );
7254             int nbStick = 0;
7255             uniqueNodes.clear();
7256             for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7257               if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7258                 nbStick++;
7259               else
7260                 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7261             }
7262             if ( nbStick == 1 ) {
7263               // ... and the opposite one - into a triangle.
7264               // set a top node
7265               uniqueNodes.push_back( curNodes[ pickInd ]);
7266               toRemove = false;
7267             }
7268             break;
7269           }
7270         }
7271       }
7272       else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7273         //////////////////////// HEX ---> prism
7274         int nbTria = 0, iTria[3];
7275         const int *ind; // indices of face nodes
7276         // look for triangular faces
7277         for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7278           ind = hexa.GetFaceNodesIndices( iFace );
7279           TIDSortedNodeSet faceNodes;
7280           for ( iCur = 0; iCur < 4; iCur++ )
7281             faceNodes.insert( curNodes[ind[iCur]] );
7282           if ( faceNodes.size() == 3 )
7283             iTria[ nbTria++ ] = iFace;
7284         }
7285         // check if triangles are opposite
7286         if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7287         {
7288           // set nodes of the bottom triangle
7289           ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7290           vector<int> indB;
7291           for ( iCur = 0; iCur < 4; iCur++ )
7292             if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7293               indB.push_back( ind[iCur] );
7294           if ( !hexa.IsForward() )
7295             std::swap( indB[0], indB[2] );
7296           for ( iCur = 0; iCur < 3; iCur++ )
7297             uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7298           // set nodes of the top triangle
7299           const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7300           for ( iCur = 0; iCur < 3; ++iCur )
7301             for ( int j = 0; j < 4; ++j )
7302               if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7303               {
7304                 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7305                 break;
7306               }
7307           toRemove = false;
7308           break;
7309         }
7310       }
7311       else if (nbUniqueNodes == 5 && nbRepl == 3 ) {
7312         //////////////////// HEXAHEDRON ---> pyramid
7313         for ( int iFace = 0; iFace < 6; iFace++ ) {
7314           const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7315           if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7316               curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7317               curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7318             // one face turns into a point ...
7319             int iOppFace = hexa.GetOppFaceIndex( iFace );
7320             ind = hexa.GetFaceNodesIndices( iOppFace );
7321             uniqueNodes.clear();
7322             for ( iCur = 0; iCur < 4; iCur++ ) {
7323               if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7324                 break;
7325               else
7326                 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7327             }
7328             if ( uniqueNodes.size() == 4 ) {
7329               // ... and the opposite one is a quadrangle
7330               // set a top node
7331               const int* indTop = hexa.GetFaceNodesIndices( iFace );
7332               uniqueNodes.push_back( curNodes[indTop[ 0 ]]);
7333               toRemove = false;
7334             }
7335             break;
7336           }
7337         }
7338       }
7339
7340       if ( toRemove && nbUniqueNodes > 4 ) {
7341         ////////////////// HEXAHEDRON ---> polyhedron
7342         hexa.SetExternalNormal();
7343         vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7344         vector<int>                  & quantities = newElemDefs[0].myPolyhedQuantities;
7345         poly_nodes.reserve( 6 * 4 ); poly_nodes.clear();
7346         quantities.reserve( 6 );     quantities.clear();
7347         for ( int iFace = 0; iFace < 6; iFace++ )
7348         {
7349           const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7350           if ( curNodes[ind[0]] == curNodes[ind[2]] ||
7351                curNodes[ind[1]] == curNodes[ind[3]] )
7352           {
7353             quantities.clear();
7354             break; // opposite nodes stick
7355           }
7356           nodeSet.clear();
7357           for ( iCur = 0; iCur < 4; iCur++ )
7358           {
7359             if ( nodeSet.insert( curNodes[ind[ iCur ]] ).second )
7360               poly_nodes.push_back( curNodes[ind[ iCur ]]);
7361           }
7362           if ( nodeSet.size() < 3 )
7363             poly_nodes.resize( poly_nodes.size() - nodeSet.size() );
7364           else
7365             quantities.push_back( nodeSet.size() );
7366         }
7367         if ( quantities.size() >= 4 )
7368         {
7369           nbResElems = 1;
7370           nbUniqueNodes = poly_nodes.size();
7371           newElemDefs[0].SetPoly(true);
7372         }
7373       }
7374       break;
7375     } // case HEXAHEDRON
7376
7377     default:
7378       toRemove = true;
7379
7380     } // switch ( entity )
7381
7382     if ( toRemove && nbResElems == 0 && avoidMakingHoles )
7383     {
7384       // erase from nodeNodeMap nodes whose merge spoils elem
7385       vector< const SMDS_MeshNode* > noMergeNodes;
7386       SMESH_MeshAlgos::DeMerge( elem, curNodes, noMergeNodes );
7387       for ( size_t i = 0; i < noMergeNodes.size(); ++i )
7388         nodeNodeMap.erase( noMergeNodes[i] );
7389     }
7390     
7391   } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7392
7393   uniqueNodes.resize( nbUniqueNodes );
7394
7395   if ( !toRemove && nbResElems == 0 )
7396     nbResElems = 1;
7397
7398   newElemDefs.resize( nbResElems );
7399
7400   return !toRemove;
7401 }
7402
7403
7404 // ========================================================
7405 // class   : ComparableElement
7406 // purpose : allow comparing elements basing on their nodes
7407 // ========================================================
7408
7409 class ComparableElement : public boost::container::flat_set< int >
7410 {
7411   typedef boost::container::flat_set< int >  int_set;
7412
7413   const SMDS_MeshElement* myElem;
7414   int                     mySumID;
7415   mutable int             myGroupID;
7416
7417 public:
7418
7419   ComparableElement( const SMDS_MeshElement* theElem ):
7420     myElem ( theElem ), mySumID( 0 ), myGroupID( -1 )
7421   {
7422     this->reserve( theElem->NbNodes() );
7423     for ( SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator(); nodeIt->more(); )
7424     {
7425       int id = nodeIt->next()->GetID();
7426       mySumID += id;
7427       this->insert( id );
7428     }
7429   }
7430
7431   const SMDS_MeshElement* GetElem() const { return myElem; }
7432
7433   int& GroupID() const { return myGroupID; }
7434   //int& GroupID() const { return const_cast< int& >( myGroupID ); }
7435
7436   ComparableElement( const ComparableElement& theSource ) // move copy
7437   {
7438     ComparableElement& src = const_cast< ComparableElement& >( theSource );
7439     (int_set&) (*this ) = boost::move( src );
7440     myElem    = src.myElem;
7441     mySumID   = src.mySumID;
7442     myGroupID = src.myGroupID;
7443   }
7444
7445   static int HashCode(const ComparableElement& se, int limit )
7446   {
7447     return ::HashCode( se.mySumID, limit );
7448   }
7449   static Standard_Boolean IsEqual(const ComparableElement& se1, const ComparableElement& se2 )
7450   {
7451     return ( se1 == se2 );
7452   }
7453
7454 };
7455
7456 //=======================================================================
7457 //function : FindEqualElements
7458 //purpose  : Return list of group of elements built on the same nodes.
7459 //           Search among theElements or in the whole mesh if theElements is empty
7460 //=======================================================================
7461
7462 void SMESH_MeshEditor::FindEqualElements( TIDSortedElemSet &        theElements,
7463                                           TListOfListOfElementsID & theGroupsOfElementsID )
7464 {
7465   ClearLastCreated();
7466
7467   SMDS_ElemIteratorPtr elemIt;
7468   if ( theElements.empty() ) elemIt = GetMeshDS()->elementsIterator();
7469   else                       elemIt = SMESHUtils::elemSetIterator( theElements );
7470
7471   typedef NCollection_Map< ComparableElement, ComparableElement > TMapOfElements;
7472   typedef std::list<int>                                          TGroupOfElems;
7473   TMapOfElements               mapOfElements;
7474   std::vector< TGroupOfElems > arrayOfGroups;
7475   TGroupOfElems                groupOfElems;
7476
7477   while ( elemIt->more() )
7478   {
7479     const SMDS_MeshElement* curElem = elemIt->next();
7480     if ( curElem->IsNull() )
7481       continue;
7482     ComparableElement      compElem = curElem;
7483     // check uniqueness
7484     const ComparableElement& elemInSet = mapOfElements.Added( compElem );
7485     if ( elemInSet.GetElem() != curElem ) // coincident elem
7486     {
7487       int& iG = elemInSet.GroupID();
7488       if ( iG < 0 )
7489       {
7490         iG = arrayOfGroups.size();
7491         arrayOfGroups.push_back( groupOfElems );
7492         arrayOfGroups[ iG ].push_back( elemInSet.GetElem()->GetID() );
7493       }
7494       arrayOfGroups[ iG ].push_back( curElem->GetID() );
7495     }
7496   }
7497
7498   groupOfElems.clear();
7499   std::vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
7500   for ( ; groupIt != arrayOfGroups.end(); ++groupIt )
7501   {
7502     if ( groupIt->size() > 1 ) {
7503       //groupOfElems.sort(); -- theElements are sorted already
7504       theGroupsOfElementsID.emplace_back( *groupIt );
7505     }
7506   }
7507 }
7508
7509 //=======================================================================
7510 //function : MergeElements
7511 //purpose  : In each given group, substitute all elements by the first one.
7512 //=======================================================================
7513
7514 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
7515 {
7516   ClearLastCreated();
7517
7518   typedef list<int> TListOfIDs;
7519   TListOfIDs rmElemIds; // IDs of elems to remove
7520
7521   SMESHDS_Mesh* aMesh = GetMeshDS();
7522
7523   TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
7524   while ( groupsIt != theGroupsOfElementsID.end() ) {
7525     TListOfIDs& aGroupOfElemID = *groupsIt;
7526     aGroupOfElemID.sort();
7527     int elemIDToKeep = aGroupOfElemID.front();
7528     const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
7529     aGroupOfElemID.pop_front();
7530     TListOfIDs::iterator idIt = aGroupOfElemID.begin();
7531     while ( idIt != aGroupOfElemID.end() ) {
7532       int elemIDToRemove = *idIt;
7533       const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
7534       // add the kept element in groups of removed one (PAL15188)
7535       AddToSameGroups( elemToKeep, elemToRemove, aMesh );
7536       rmElemIds.push_back( elemIDToRemove );
7537       ++idIt;
7538     }
7539     ++groupsIt;
7540   }
7541
7542   Remove( rmElemIds, false );
7543 }
7544
7545 //=======================================================================
7546 //function : MergeEqualElements
7547 //purpose  : Remove all but one of elements built on the same nodes.
7548 //=======================================================================
7549
7550 void SMESH_MeshEditor::MergeEqualElements()
7551 {
7552   TIDSortedElemSet aMeshElements; /* empty input ==
7553                                      to merge equal elements in the whole mesh */
7554   TListOfListOfElementsID aGroupsOfElementsID;
7555   FindEqualElements( aMeshElements, aGroupsOfElementsID );
7556   MergeElements( aGroupsOfElementsID );
7557 }
7558
7559 //=======================================================================
7560 //function : findAdjacentFace
7561 //purpose  :
7562 //=======================================================================
7563
7564 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
7565                                                 const SMDS_MeshNode* n2,
7566                                                 const SMDS_MeshElement* elem)
7567 {
7568   TIDSortedElemSet elemSet, avoidSet;
7569   if ( elem )
7570     avoidSet.insert ( elem );
7571   return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
7572 }
7573
7574 //=======================================================================
7575 //function : findSegment
7576 //purpose  : Return a mesh segment by two nodes one of which can be medium
7577 //=======================================================================
7578
7579 static const SMDS_MeshElement* findSegment(const SMDS_MeshNode* n1,
7580                                            const SMDS_MeshNode* n2)
7581 {
7582   SMDS_ElemIteratorPtr it = n1->GetInverseElementIterator( SMDSAbs_Edge );
7583   while ( it->more() )
7584   {
7585     const SMDS_MeshElement* seg = it->next();
7586     if ( seg->GetNodeIndex( n2 ) >= 0 )
7587       return seg;
7588   }
7589   return 0;
7590 }
7591
7592 //=======================================================================
7593 //function : FindFreeBorder
7594 //purpose  :
7595 //=======================================================================
7596
7597 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
7598
7599 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode*             theFirstNode,
7600                                        const SMDS_MeshNode*             theSecondNode,
7601                                        const SMDS_MeshNode*             theLastNode,
7602                                        list< const SMDS_MeshNode* > &   theNodes,
7603                                        list< const SMDS_MeshElement* >& theFaces)
7604 {
7605   if ( !theFirstNode || !theSecondNode )
7606     return false;
7607   // find border face between theFirstNode and theSecondNode
7608   const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
7609   if ( !curElem )
7610     return false;
7611
7612   theFaces.push_back( curElem );
7613   theNodes.push_back( theFirstNode );
7614   theNodes.push_back( theSecondNode );
7615
7616   const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
7617   //TIDSortedElemSet foundElems;
7618   bool needTheLast = ( theLastNode != 0 );
7619
7620   vector<const SMDS_MeshNode*> nodes;
7621   
7622   while ( nStart != theLastNode ) {
7623     if ( nStart == theFirstNode )
7624       return !needTheLast;
7625
7626     // find all free border faces sharing nStart
7627
7628     list< const SMDS_MeshElement* > curElemList;
7629     list< const SMDS_MeshNode* >    nStartList;
7630     SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
7631     while ( invElemIt->more() ) {
7632       const SMDS_MeshElement* e = invElemIt->next();
7633       //if ( e == curElem || foundElems.insert( e ).second ) // e can encounter twice in border
7634       {
7635         // get nodes
7636         nodes.assign( SMDS_MeshElement::iterator( e->interlacedNodesIterator() ),
7637                       SMDS_MeshElement::iterator() );
7638         nodes.push_back( nodes[ 0 ]);
7639
7640         // check 2 links
7641         int iNode = 0, nbNodes = nodes.size() - 1;
7642         for ( iNode = 0; iNode < nbNodes; iNode++ )
7643           if ((( nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
7644                ( nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
7645               ( ControlFreeBorder( &nodes[ iNode ], e->GetID() )))
7646           {
7647             nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart )]);
7648             curElemList.push_back( e );
7649           }
7650       }
7651     }
7652     // analyse the found
7653
7654     int nbNewBorders = curElemList.size();
7655     if ( nbNewBorders == 0 ) {
7656       // no free border furthermore
7657       return !needTheLast;
7658     }
7659     else if ( nbNewBorders == 1 ) {
7660       // one more element found
7661       nIgnore = nStart;
7662       nStart = nStartList.front();
7663       curElem = curElemList.front();
7664       theFaces.push_back( curElem );
7665       theNodes.push_back( nStart );
7666     }
7667     else {
7668       // several continuations found
7669       list< const SMDS_MeshElement* >::iterator curElemIt;
7670       list< const SMDS_MeshNode* >::iterator nStartIt;
7671       // check if one of them reached the last node
7672       if ( needTheLast ) {
7673         for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7674              curElemIt!= curElemList.end();
7675              curElemIt++, nStartIt++ )
7676           if ( *nStartIt == theLastNode ) {
7677             theFaces.push_back( *curElemIt );
7678             theNodes.push_back( *nStartIt );
7679             return true;
7680           }
7681       }
7682       // find the best free border by the continuations
7683       list<const SMDS_MeshNode*>    contNodes[ 2 ], *cNL;
7684       list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
7685       for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7686            curElemIt!= curElemList.end();
7687            curElemIt++, nStartIt++ )
7688       {
7689         cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
7690         cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
7691         // find one more free border
7692         if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
7693           cNL->clear();
7694           cFL->clear();
7695         }
7696         else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
7697           // choice: clear a worse one
7698           int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
7699           int   iWorse = ( needTheLast ? 1 - iLongest : iLongest );
7700           contNodes[ iWorse ].clear();
7701           contFaces[ iWorse ].clear();
7702         }
7703       }
7704       if ( contNodes[0].empty() && contNodes[1].empty() )
7705         return false;
7706
7707       // push_back the best free border
7708       cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
7709       cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
7710       //theNodes.pop_back(); // remove nIgnore
7711       theNodes.pop_back(); // remove nStart
7712       //theFaces.pop_back(); // remove curElem
7713       theNodes.splice( theNodes.end(), *cNL );
7714       theFaces.splice( theFaces.end(), *cFL );
7715       return true;
7716
7717     } // several continuations found
7718   } // while ( nStart != theLastNode )
7719
7720   return true;
7721 }
7722
7723 //=======================================================================
7724 //function : CheckFreeBorderNodes
7725 //purpose  : Return true if the tree nodes are on a free border
7726 //=======================================================================
7727
7728 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
7729                                             const SMDS_MeshNode* theNode2,
7730                                             const SMDS_MeshNode* theNode3)
7731 {
7732   list< const SMDS_MeshNode* > nodes;
7733   list< const SMDS_MeshElement* > faces;
7734   return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
7735 }
7736
7737 //=======================================================================
7738 //function : SewFreeBorder
7739 //purpose  :
7740 //warning  : for border-to-side sewing theSideSecondNode is considered as
7741 //           the last side node and theSideThirdNode is not used
7742 //=======================================================================
7743
7744 SMESH_MeshEditor::Sew_Error
7745 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
7746                                  const SMDS_MeshNode* theBordSecondNode,
7747                                  const SMDS_MeshNode* theBordLastNode,
7748                                  const SMDS_MeshNode* theSideFirstNode,
7749                                  const SMDS_MeshNode* theSideSecondNode,
7750                                  const SMDS_MeshNode* theSideThirdNode,
7751                                  const bool           theSideIsFreeBorder,
7752                                  const bool           toCreatePolygons,
7753                                  const bool           toCreatePolyedrs)
7754 {
7755   ClearLastCreated();
7756
7757   Sew_Error aResult = SEW_OK;
7758
7759   // ====================================
7760   //    find side nodes and elements
7761   // ====================================
7762
7763   list< const SMDS_MeshNode* >    nSide[ 2 ];
7764   list< const SMDS_MeshElement* > eSide[ 2 ];
7765   list< const SMDS_MeshNode* >::iterator    nIt[ 2 ];
7766   list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
7767
7768   // Free border 1
7769   // --------------
7770   if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
7771                       nSide[0], eSide[0])) {
7772     MESSAGE(" Free Border 1 not found " );
7773     aResult = SEW_BORDER1_NOT_FOUND;
7774   }
7775   if (theSideIsFreeBorder) {
7776     // Free border 2
7777     // --------------
7778     if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
7779                         nSide[1], eSide[1])) {
7780       MESSAGE(" Free Border 2 not found " );
7781       aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
7782     }
7783   }
7784   if ( aResult != SEW_OK )
7785     return aResult;
7786
7787   if (!theSideIsFreeBorder) {
7788     // Side 2
7789     // --------------
7790
7791     // -------------------------------------------------------------------------
7792     // Algo:
7793     // 1. If nodes to merge are not coincident, move nodes of the free border
7794     //    from the coord sys defined by the direction from the first to last
7795     //    nodes of the border to the correspondent sys of the side 2
7796     // 2. On the side 2, find the links most co-directed with the correspondent
7797     //    links of the free border
7798     // -------------------------------------------------------------------------
7799
7800     // 1. Since sewing may break if there are volumes to split on the side 2,
7801     //    we won't move nodes but just compute new coordinates for them
7802     typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
7803     TNodeXYZMap nBordXYZ;
7804     list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
7805     list< const SMDS_MeshNode* >::iterator nBordIt;
7806
7807     gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
7808     gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
7809     gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
7810     gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
7811     double tol2 = 1.e-8;
7812     gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
7813     if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
7814       // Need node movement.
7815
7816       // find X and Z axes to create trsf
7817       gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
7818       gp_Vec X = Zs ^ Zb;
7819       if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
7820         // Zb || Zs
7821         X = gp_Ax2( gp::Origin(), Zb ).XDirection();
7822
7823       // coord systems
7824       gp_Ax3 toBordAx( Pb1, Zb, X );
7825       gp_Ax3 fromSideAx( Ps1, Zs, X );
7826       gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
7827       // set trsf
7828       gp_Trsf toBordSys, fromSide2Sys;
7829       toBordSys.SetTransformation( toBordAx );
7830       fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
7831       fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
7832
7833       // move
7834       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
7835         const SMDS_MeshNode* n = *nBordIt;
7836         gp_XYZ xyz( n->X(),n->Y(),n->Z() );
7837         toBordSys.Transforms( xyz );
7838         fromSide2Sys.Transforms( xyz );
7839         nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
7840       }
7841     }
7842     else {
7843       // just insert nodes XYZ in the nBordXYZ map
7844       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
7845         const SMDS_MeshNode* n = *nBordIt;
7846         nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
7847       }
7848     }
7849
7850     // 2. On the side 2, find the links most co-directed with the correspondent
7851     //    links of the free border
7852
7853     list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
7854     list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
7855     sideNodes.push_back( theSideFirstNode );
7856
7857     bool hasVolumes = false;
7858     LinkID_Gen aLinkID_Gen( GetMeshDS() );
7859     set<long> foundSideLinkIDs, checkedLinkIDs;
7860     SMDS_VolumeTool volume;
7861     //const SMDS_MeshNode* faceNodes[ 4 ];
7862
7863     const SMDS_MeshNode*    sideNode;
7864     const SMDS_MeshElement* sideElem  = 0;
7865     const SMDS_MeshNode* prevSideNode = theSideFirstNode;
7866     const SMDS_MeshNode* prevBordNode = theBordFirstNode;
7867     nBordIt = bordNodes.begin();
7868     nBordIt++;
7869     // border node position and border link direction to compare with
7870     gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
7871     gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
7872     // choose next side node by link direction or by closeness to
7873     // the current border node:
7874     bool searchByDir = ( *nBordIt != theBordLastNode );
7875     do {
7876       // find the next node on the Side 2
7877       sideNode = 0;
7878       double maxDot = -DBL_MAX, minDist = DBL_MAX;
7879       long linkID;
7880       checkedLinkIDs.clear();
7881       gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
7882
7883       // loop on inverse elements of current node (prevSideNode) on the Side 2
7884       SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
7885       while ( invElemIt->more() )
7886       {
7887         const SMDS_MeshElement* elem = invElemIt->next();
7888         // prepare data for a loop on links coming to prevSideNode, of a face or a volume
7889         int iPrevNode = 0, iNode = 0, nbNodes = elem->NbNodes();
7890         vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
7891         bool isVolume = volume.Set( elem );
7892         const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
7893         if ( isVolume ) // --volume
7894           hasVolumes = true;
7895         else if ( elem->GetType() == SMDSAbs_Face ) { // --face
7896           // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
7897           SMDS_NodeIteratorPtr nIt = elem->interlacedNodesIterator();
7898           while ( nIt->more() ) {
7899             nodes[ iNode ] = cast2Node( nIt->next() );
7900             if ( nodes[ iNode++ ] == prevSideNode )
7901               iPrevNode = iNode - 1;
7902           }
7903           // there are 2 links to check
7904           nbNodes = 2;
7905         }
7906         else // --edge
7907           continue;
7908         // loop on links, to be precise, on the second node of links
7909         for ( iNode = 0; iNode < nbNodes; iNode++ ) {
7910           const SMDS_MeshNode* n = nodes[ iNode ];
7911           if ( isVolume ) {
7912             if ( !volume.IsLinked( n, prevSideNode ))
7913               continue;
7914           }
7915           else {
7916             if ( iNode ) // a node before prevSideNode
7917               n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
7918             else         // a node after prevSideNode
7919               n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
7920           }
7921           // check if this link was already used
7922           long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
7923           bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
7924           if (!isJustChecked &&
7925               foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
7926           {
7927             // test a link geometrically
7928             gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
7929             bool linkIsBetter = false;
7930             double dot = 0.0, dist = 0.0;
7931             if ( searchByDir ) { // choose most co-directed link
7932               dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
7933               linkIsBetter = ( dot > maxDot );
7934             }
7935             else { // choose link with the node closest to bordPos
7936               dist = ( nextXYZ - bordPos ).SquareModulus();
7937               linkIsBetter = ( dist < minDist );
7938             }
7939             if ( linkIsBetter ) {
7940               maxDot = dot;
7941               minDist = dist;
7942               linkID = iLink;
7943               sideNode = n;
7944               sideElem = elem;
7945             }
7946           }
7947         }
7948       } // loop on inverse elements of prevSideNode
7949
7950       if ( !sideNode ) {
7951         MESSAGE(" Can't find path by links of the Side 2 ");
7952         return SEW_BAD_SIDE_NODES;
7953       }
7954       sideNodes.push_back( sideNode );
7955       sideElems.push_back( sideElem );
7956       foundSideLinkIDs.insert ( linkID );
7957       prevSideNode = sideNode;
7958
7959       if ( *nBordIt == theBordLastNode )
7960         searchByDir = false;
7961       else {
7962         // find the next border link to compare with
7963         gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
7964         searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
7965         // move to next border node if sideNode is before forward border node (bordPos)
7966         while ( *nBordIt != theBordLastNode && !searchByDir ) {
7967           prevBordNode = *nBordIt;
7968           nBordIt++;
7969           bordPos = nBordXYZ[ *nBordIt ];
7970           bordDir = bordPos - nBordXYZ[ prevBordNode ];
7971           searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
7972         }
7973       }
7974     }
7975     while ( sideNode != theSideSecondNode );
7976
7977     if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
7978       MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
7979       return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
7980     }
7981   } // end nodes search on the side 2
7982
7983   // ============================
7984   // sew the border to the side 2
7985   // ============================
7986
7987   int nbNodes[]  = { (int)nSide[0].size(), (int)nSide[1].size() };
7988   int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
7989
7990   bool toMergeConformal = ( nbNodes[0] == nbNodes[1] );
7991   if ( toMergeConformal && toCreatePolygons )
7992   {
7993     // do not merge quadrangles if polygons are OK (IPAL0052824)
7994     eIt[0] = eSide[0].begin();
7995     eIt[1] = eSide[1].begin();
7996     bool allQuads[2] = { true, true };
7997     for ( int iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
7998       for ( ; allQuads[iBord] && eIt[iBord] != eSide[iBord].end(); ++eIt[iBord] )
7999         allQuads[iBord] = ( (*eIt[iBord])->NbCornerNodes() == 4 );
8000     }
8001     toMergeConformal = ( !allQuads[0] && !allQuads[1] );
8002   }
8003
8004   TListOfListOfNodes nodeGroupsToMerge;
8005   if (( toMergeConformal ) ||
8006       ( theSideIsFreeBorder && !theSideThirdNode )) {
8007
8008     // all nodes are to be merged
8009
8010     for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8011          nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8012          nIt[0]++, nIt[1]++ )
8013     {
8014       nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8015       nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8016       nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8017     }
8018   }
8019   else {
8020
8021     // insert new nodes into the border and the side to get equal nb of segments
8022
8023     // get normalized parameters of nodes on the borders
8024     vector< double > param[ 2 ];
8025     param[0].resize( maxNbNodes );
8026     param[1].resize( maxNbNodes );
8027     int iNode, iBord;
8028     for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8029       list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8030       list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8031       const SMDS_MeshNode* nPrev = *nIt;
8032       double bordLength = 0;
8033       for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8034         const SMDS_MeshNode* nCur = *nIt;
8035         gp_XYZ segment (nCur->X() - nPrev->X(),
8036                         nCur->Y() - nPrev->Y(),
8037                         nCur->Z() - nPrev->Z());
8038         double segmentLen = segment.Modulus();
8039         bordLength += segmentLen;
8040         param[ iBord ][ iNode ] = bordLength;
8041         nPrev = nCur;
8042       }
8043       // normalize within [0,1]
8044       for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8045         param[ iBord ][ iNode ] /= bordLength;
8046       }
8047     }
8048
8049     // loop on border segments
8050     const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8051     int i[ 2 ] = { 0, 0 };
8052     nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8053     nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8054
8055     // element can be split while iterating on border if it has two edges in the border
8056     std::map< const SMDS_MeshElement* , const SMDS_MeshElement* > elemReplaceMap;
8057     std::map< const SMDS_MeshElement* , const SMDS_MeshElement* >::iterator elemReplaceMapIt;
8058
8059     TElemOfNodeListMap insertMap;
8060     TElemOfNodeListMap::iterator insertMapIt;
8061     // insertMap is
8062     // key:   elem to insert nodes into
8063     // value: 2 nodes to insert between + nodes to be inserted
8064     do {
8065       bool next[ 2 ] = { false, false };
8066
8067       // find min adjacent segment length after sewing
8068       double nextParam = 10., prevParam = 0;
8069       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8070         if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8071           nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8072         if ( i[ iBord ] > 0 )
8073           prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8074       }
8075       double  minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8076       double  maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8077       double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8078
8079       // choose to insert or to merge nodes
8080       double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8081       if ( Abs( du ) <= minSegLen * 0.2 ) {
8082         // merge
8083         // ------
8084         nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8085         const SMDS_MeshNode* n0 = *nIt[0];
8086         const SMDS_MeshNode* n1 = *nIt[1];
8087         nodeGroupsToMerge.back().push_back( n1 );
8088         nodeGroupsToMerge.back().push_back( n0 );
8089         // position of node of the border changes due to merge
8090         param[ 0 ][ i[0] ] += du;
8091         // move n1 for the sake of elem shape evaluation during insertion.
8092         // n1 will be removed by MergeNodes() anyway
8093         const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8094         next[0] = next[1] = true;
8095       }
8096       else {
8097         // insert
8098         // ------
8099         int intoBord = ( du < 0 ) ? 0 : 1;
8100         const SMDS_MeshElement* elem = *eIt [ intoBord ];
8101         const SMDS_MeshNode*    n1   = nPrev[ intoBord ];
8102         const SMDS_MeshNode*    n2   = *nIt [ intoBord ];
8103         const SMDS_MeshNode*    nIns = *nIt [ 1 - intoBord ];
8104         if ( intoBord == 1 ) {
8105           // move node of the border to be on a link of elem of the side
8106           SMESH_NodeXYZ p1( n1 ), p2( n2 );
8107           double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8108           gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8109           GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8110         }
8111         elemReplaceMapIt = elemReplaceMap.find( elem );
8112         if ( elemReplaceMapIt != elemReplaceMap.end() )
8113           elem = elemReplaceMapIt->second;
8114
8115         insertMapIt = insertMap.find( elem );
8116         bool  notFound = ( insertMapIt == insertMap.end() );
8117         bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8118         if ( otherLink ) {
8119           // insert into another link of the same element:
8120           // 1. perform insertion into the other link of the elem
8121           list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8122           const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8123           const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8124           InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8125           // 2. perform insertion into the link of adjacent faces
8126           while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem )) {
8127             InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8128           }
8129           while ( const SMDS_MeshElement* seg = findSegment( n12, n22 )) {
8130             InsertNodesIntoLink( seg, n12, n22, nodeList );
8131           }
8132           if (toCreatePolyedrs) {
8133             // perform insertion into the links of adjacent volumes
8134             UpdateVolumes(n12, n22, nodeList);
8135           }
8136           // 3. find an element appeared on n1 and n2 after the insertion
8137           insertMap.erase( insertMapIt );
8138           const SMDS_MeshElement* elem2 = findAdjacentFace( n1, n2, 0 );
8139           elemReplaceMap.insert( std::make_pair( elem, elem2 ));
8140           elem = elem2;
8141         }
8142         if ( notFound || otherLink ) {
8143           // add element and nodes of the side into the insertMap
8144           insertMapIt = insertMap.insert( make_pair( elem, list<const SMDS_MeshNode*>() )).first;
8145           (*insertMapIt).second.push_back( n1 );
8146           (*insertMapIt).second.push_back( n2 );
8147         }
8148         // add node to be inserted into elem
8149         (*insertMapIt).second.push_back( nIns );
8150         next[ 1 - intoBord ] = true;
8151       }
8152
8153       // go to the next segment
8154       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8155         if ( next[ iBord ] ) {
8156           if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8157             eIt[ iBord ]++;
8158           nPrev[ iBord ] = *nIt[ iBord ];
8159           nIt[ iBord ]++; i[ iBord ]++;
8160         }
8161       }
8162     }
8163     while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8164
8165     // perform insertion of nodes into elements
8166
8167     for (insertMapIt = insertMap.begin();
8168          insertMapIt != insertMap.end();
8169          insertMapIt++ )
8170     {
8171       const SMDS_MeshElement* elem = (*insertMapIt).first;
8172       list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8173       if ( nodeList.size() < 3 ) continue;
8174       const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8175       const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8176
8177       InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8178
8179       while ( const SMDS_MeshElement* seg = findSegment( n1, n2 )) {
8180         InsertNodesIntoLink( seg, n1, n2, nodeList );
8181       }
8182
8183       if ( !theSideIsFreeBorder ) {
8184         // look for and insert nodes into the faces adjacent to elem
8185         while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem )) {
8186           InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8187         }
8188       }
8189       if (toCreatePolyedrs) {
8190         // perform insertion into the links of adjacent volumes
8191         UpdateVolumes(n1, n2, nodeList);
8192       }
8193     }
8194   } // end: insert new nodes
8195
8196   MergeNodes ( nodeGroupsToMerge );
8197
8198
8199   // Remove coincident segments
8200
8201   // get new segments
8202   TIDSortedElemSet segments;
8203   SMESH_SequenceOfElemPtr newFaces;
8204   for ( size_t i = 0; i < myLastCreatedElems.size(); ++i )
8205   {
8206     if ( !myLastCreatedElems[i] ) continue;
8207     if ( myLastCreatedElems[i]->GetType() == SMDSAbs_Edge )
8208       segments.insert( segments.end(), myLastCreatedElems[i] );
8209     else
8210       newFaces.push_back( myLastCreatedElems[i] );
8211   }
8212   // get segments adjacent to merged nodes
8213   TListOfListOfNodes::iterator groupIt = nodeGroupsToMerge.begin();
8214   for ( ; groupIt != nodeGroupsToMerge.end(); groupIt++ )
8215   {
8216     const list<const SMDS_MeshNode*>& nodes = *groupIt;
8217     if ( nodes.front()->IsNull() ) continue;
8218     SMDS_ElemIteratorPtr segIt = nodes.front()->GetInverseElementIterator( SMDSAbs_Edge );
8219     while ( segIt->more() )
8220       segments.insert( segIt->next() );
8221   }
8222
8223   // find coincident
8224   TListOfListOfElementsID equalGroups;
8225   if ( !segments.empty() )
8226     FindEqualElements( segments, equalGroups );
8227   if ( !equalGroups.empty() )
8228   {
8229     // remove from segments those that will be removed
8230     TListOfListOfElementsID::iterator itGroups = equalGroups.begin();
8231     for ( ; itGroups != equalGroups.end(); ++itGroups )
8232     {
8233       list< int >& group = *itGroups;
8234       list< int >::iterator id = group.begin();
8235       for ( ++id; id != group.end(); ++id )
8236         if ( const SMDS_MeshElement* seg = GetMeshDS()->FindElement( *id ))
8237           segments.erase( seg );
8238     }
8239     // remove equal segments
8240     MergeElements( equalGroups );
8241
8242     // restore myLastCreatedElems
8243     myLastCreatedElems = newFaces;
8244     TIDSortedElemSet::iterator seg = segments.begin();
8245     for ( ; seg != segments.end(); ++seg )
8246       myLastCreatedElems.push_back( *seg );
8247   }
8248
8249   return aResult;
8250 }
8251
8252 //=======================================================================
8253 //function : InsertNodesIntoLink
8254 //purpose  : insert theNodesToInsert into theElement between theBetweenNode1
8255 //           and theBetweenNode2 and split theElement
8256 //=======================================================================
8257
8258 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement*     theElement,
8259                                            const SMDS_MeshNode*        theBetweenNode1,
8260                                            const SMDS_MeshNode*        theBetweenNode2,
8261                                            list<const SMDS_MeshNode*>& theNodesToInsert,
8262                                            const bool                  toCreatePoly)
8263 {
8264   if ( !theElement ) return;
8265
8266   SMESHDS_Mesh *aMesh = GetMeshDS();
8267   vector<const SMDS_MeshElement*> newElems;
8268
8269   if ( theElement->GetType() == SMDSAbs_Edge )
8270   {
8271     theNodesToInsert.push_front( theBetweenNode1 );
8272     theNodesToInsert.push_back ( theBetweenNode2 );
8273     list<const SMDS_MeshNode*>::iterator n = theNodesToInsert.begin();
8274     const SMDS_MeshNode* n1 = *n;
8275     for ( ++n; n != theNodesToInsert.end(); ++n )
8276     {
8277       const SMDS_MeshNode* n2 = *n;
8278       if ( const SMDS_MeshElement* seg = aMesh->FindEdge( n1, n2 ))
8279         AddToSameGroups( seg, theElement, aMesh );
8280       else
8281         newElems.push_back( aMesh->AddEdge ( n1, n2 ));
8282       n1 = n2;
8283     }
8284     theNodesToInsert.pop_front();
8285     theNodesToInsert.pop_back();
8286
8287     if ( theElement->IsQuadratic() ) // add a not split part
8288     {
8289       vector<const SMDS_MeshNode*> nodes( theElement->begin_nodes(),
8290                                           theElement->end_nodes() );
8291       int iOther = 0, nbN = nodes.size();
8292       for ( ; iOther < nbN; ++iOther )
8293         if ( nodes[iOther] != theBetweenNode1 &&
8294              nodes[iOther] != theBetweenNode2 )
8295           break;
8296       if      ( iOther == 0 )
8297       {
8298         if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[0], nodes[1] ))
8299           AddToSameGroups( seg, theElement, aMesh );
8300         else
8301           newElems.push_back( aMesh->AddEdge ( nodes[0], nodes[1] ));
8302       }
8303       else if ( iOther == 2 )
8304       {
8305         if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[1], nodes[2] ))
8306           AddToSameGroups( seg, theElement, aMesh );
8307         else
8308           newElems.push_back( aMesh->AddEdge ( nodes[1], nodes[2] ));
8309       }
8310     }
8311     // treat new elements
8312     for ( size_t i = 0; i < newElems.size(); ++i )
8313       if ( newElems[i] )
8314       {
8315         aMesh->SetMeshElementOnShape( newElems[i], theElement->getshapeId() );
8316         myLastCreatedElems.push_back( newElems[i] );
8317       }
8318     ReplaceElemInGroups( theElement, newElems, aMesh );
8319     aMesh->RemoveElement( theElement );
8320     return;
8321
8322   } // if ( theElement->GetType() == SMDSAbs_Edge )
8323
8324   const SMDS_MeshElement* theFace = theElement;
8325   if ( theFace->GetType() != SMDSAbs_Face ) return;
8326
8327   // find indices of 2 link nodes and of the rest nodes
8328   int iNode = 0, il1, il2, i3, i4;
8329   il1 = il2 = i3 = i4 = -1;
8330   vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8331
8332   SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8333   while ( nodeIt->more() ) {
8334     const SMDS_MeshNode* n = nodeIt->next();
8335     if ( n == theBetweenNode1 )
8336       il1 = iNode;
8337     else if ( n == theBetweenNode2 )
8338       il2 = iNode;
8339     else if ( i3 < 0 )
8340       i3 = iNode;
8341     else
8342       i4 = iNode;
8343     nodes[ iNode++ ] = n;
8344   }
8345   if ( il1 < 0 || il2 < 0 || i3 < 0 )
8346     return ;
8347
8348   // arrange link nodes to go one after another regarding the face orientation
8349   bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8350   list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8351   if ( reverse ) {
8352     iNode = il1;
8353     il1 = il2;
8354     il2 = iNode;
8355     aNodesToInsert.reverse();
8356   }
8357   // check that not link nodes of a quadrangles are in good order
8358   int nbFaceNodes = theFace->NbNodes();
8359   if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8360     iNode = i3;
8361     i3 = i4;
8362     i4 = iNode;
8363   }
8364
8365   if (toCreatePoly || theFace->IsPoly()) {
8366
8367     iNode = 0;
8368     vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8369
8370     // add nodes of face up to first node of link
8371     bool isFLN = false;
8372     SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8373     while ( nodeIt->more() && !isFLN ) {
8374       const SMDS_MeshNode* n = nodeIt->next();
8375       poly_nodes[iNode++] = n;
8376       isFLN = ( n == nodes[il1] );
8377     }
8378     // add nodes to insert
8379     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8380     for (; nIt != aNodesToInsert.end(); nIt++) {
8381       poly_nodes[iNode++] = *nIt;
8382     }
8383     // add nodes of face starting from last node of link
8384     while ( nodeIt->more() ) {
8385       const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8386       poly_nodes[iNode++] = n;
8387     }
8388
8389     // make a new face
8390     newElems.push_back( aMesh->AddPolygonalFace( poly_nodes ));
8391   }
8392
8393   else if ( !theFace->IsQuadratic() )
8394   {
8395     // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8396     int nbLinkNodes = 2 + aNodesToInsert.size();
8397     //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8398     vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8399     linkNodes[ 0 ] = nodes[ il1 ];
8400     linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8401     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8402     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8403       linkNodes[ iNode++ ] = *nIt;
8404     }
8405     // decide how to split a quadrangle: compare possible variants
8406     // and choose which of splits to be a quadrangle
8407     int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad = 0;
8408     if ( nbFaceNodes == 3 ) {
8409       iBestQuad = nbSplits;
8410       i4 = i3;
8411     }
8412     else if ( nbFaceNodes == 4 ) {
8413       SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8414       double aBestRate = DBL_MAX;
8415       for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8416         i1 = 0; i2 = 1;
8417         double aBadRate = 0;
8418         // evaluate elements quality
8419         for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8420           if ( iSplit == iQuad ) {
8421             SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8422                                    linkNodes[ i2++ ],
8423                                    nodes[ i3 ],
8424                                    nodes[ i4 ]);
8425             aBadRate += getBadRate( &quad, aCrit );
8426           }
8427           else {
8428             SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8429                                    linkNodes[ i2++ ],
8430                                    nodes[ iSplit < iQuad ? i4 : i3 ]);
8431             aBadRate += getBadRate( &tria, aCrit );
8432           }
8433         }
8434         // choice
8435         if ( aBadRate < aBestRate ) {
8436           iBestQuad = iQuad;
8437           aBestRate = aBadRate;
8438         }
8439       }
8440     }
8441
8442     // create new elements
8443     i1 = 0; i2 = 1;
8444     for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ )
8445     {
8446       if ( iSplit == iBestQuad )
8447         newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8448                                             linkNodes[ i2++ ],
8449                                             nodes[ i3 ],
8450                                             nodes[ i4 ]));
8451       else
8452         newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8453                                             linkNodes[ i2++ ],
8454                                             nodes[ iSplit < iBestQuad ? i4 : i3 ]));
8455     }
8456
8457     const SMDS_MeshNode* newNodes[ 4 ];
8458     newNodes[ 0 ] = linkNodes[ i1 ];
8459     newNodes[ 1 ] = linkNodes[ i2 ];
8460     newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
8461     newNodes[ 3 ] = nodes[ i4 ];
8462     if (iSplit == iBestQuad)
8463       newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] ));
8464     else
8465       newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] ));
8466
8467   } // end if(!theFace->IsQuadratic())
8468
8469   else { // theFace is quadratic
8470     // we have to split theFace on simple triangles and one simple quadrangle
8471     int tmp = il1/2;
8472     int nbshift = tmp*2;
8473     // shift nodes in nodes[] by nbshift
8474     int i,j;
8475     for(i=0; i<nbshift; i++) {
8476       const SMDS_MeshNode* n = nodes[0];
8477       for(j=0; j<nbFaceNodes-1; j++) {
8478         nodes[j] = nodes[j+1];
8479       }
8480       nodes[nbFaceNodes-1] = n;
8481     }
8482     il1 = il1 - nbshift;
8483     // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
8484     //   n0      n1     n2    n0      n1     n2
8485     //     +-----+-----+        +-----+-----+
8486     //      \         /         |           |
8487     //       \       /          |           |
8488     //      n5+     +n3       n7+           +n3
8489     //         \   /            |           |
8490     //          \ /             |           |
8491     //           +              +-----+-----+
8492     //           n4           n6      n5     n4
8493
8494     // create new elements
8495     int n1,n2,n3;
8496     if ( nbFaceNodes == 6 ) { // quadratic triangle
8497       newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8498       if ( theFace->IsMediumNode(nodes[il1]) ) {
8499         // create quadrangle
8500         newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[5] ));
8501         n1 = 1;
8502         n2 = 2;
8503         n3 = 3;
8504       }
8505       else {
8506         // create quadrangle
8507         newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[5] ));
8508         n1 = 0;
8509         n2 = 1;
8510         n3 = 5;
8511       }
8512     }
8513     else { // nbFaceNodes==8 - quadratic quadrangle
8514       newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8515       newElems.push_back( aMesh->AddFace( nodes[5], nodes[6], nodes[7] ));
8516       newElems.push_back( aMesh->AddFace( nodes[5], nodes[7], nodes[3] ));
8517       if ( theFace->IsMediumNode( nodes[ il1 ])) {
8518         // create quadrangle
8519         newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[7] ));
8520         n1 = 1;
8521         n2 = 2;
8522         n3 = 3;
8523       }
8524       else {
8525         // create quadrangle
8526         newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[7] ));
8527         n1 = 0;
8528         n2 = 1;
8529         n3 = 7;
8530       }
8531     }
8532     // create needed triangles using n1,n2,n3 and inserted nodes
8533     int nbn = 2 + aNodesToInsert.size();
8534     vector<const SMDS_MeshNode*> aNodes(nbn);
8535     aNodes[0    ] = nodes[n1];
8536     aNodes[nbn-1] = nodes[n2];
8537     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8538     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8539       aNodes[iNode++] = *nIt;
8540     }
8541     for ( i = 1; i < nbn; i++ )
8542       newElems.push_back( aMesh->AddFace( aNodes[i-1], aNodes[i], nodes[n3] ));
8543   }
8544
8545   // remove the old face
8546   for ( size_t i = 0; i < newElems.size(); ++i )
8547     if ( newElems[i] )
8548     {
8549       aMesh->SetMeshElementOnShape( newElems[i], theFace->getshapeId() );
8550       myLastCreatedElems.push_back( newElems[i] );
8551     }
8552   ReplaceElemInGroups( theFace, newElems, aMesh );
8553   aMesh->RemoveElement(theFace);
8554
8555 } // InsertNodesIntoLink()
8556
8557 //=======================================================================
8558 //function : UpdateVolumes
8559 //purpose  :
8560 //=======================================================================
8561
8562 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode*        theBetweenNode1,
8563                                       const SMDS_MeshNode*        theBetweenNode2,
8564                                       list<const SMDS_MeshNode*>& theNodesToInsert)
8565 {
8566   ClearLastCreated();
8567
8568   SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
8569   while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
8570     const SMDS_MeshElement* elem = invElemIt->next();
8571
8572     // check, if current volume has link theBetweenNode1 - theBetweenNode2
8573     SMDS_VolumeTool aVolume (elem);
8574     if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
8575       continue;
8576
8577     // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
8578     int iface, nbFaces = aVolume.NbFaces();
8579     vector<const SMDS_MeshNode *> poly_nodes;
8580     vector<int> quantities (nbFaces);
8581
8582     for (iface = 0; iface < nbFaces; iface++) {
8583       int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
8584       // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
8585       const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
8586
8587       for (int inode = 0; inode < nbFaceNodes; inode++) {
8588         poly_nodes.push_back(faceNodes[inode]);
8589
8590         if (nbInserted == 0) {
8591           if (faceNodes[inode] == theBetweenNode1) {
8592             if (faceNodes[inode + 1] == theBetweenNode2) {
8593               nbInserted = theNodesToInsert.size();
8594
8595               // add nodes to insert
8596               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
8597               for (; nIt != theNodesToInsert.end(); nIt++) {
8598                 poly_nodes.push_back(*nIt);
8599               }
8600             }
8601           }
8602           else if (faceNodes[inode] == theBetweenNode2) {
8603             if (faceNodes[inode + 1] == theBetweenNode1) {
8604               nbInserted = theNodesToInsert.size();
8605
8606               // add nodes to insert in reversed order
8607               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
8608               nIt--;
8609               for (; nIt != theNodesToInsert.begin(); nIt--) {
8610                 poly_nodes.push_back(*nIt);
8611               }
8612               poly_nodes.push_back(*nIt);
8613             }
8614           }
8615           else {
8616           }
8617         }
8618       }
8619       quantities[iface] = nbFaceNodes + nbInserted;
8620     }
8621
8622     // Replace the volume
8623     SMESHDS_Mesh *aMesh = GetMeshDS();
8624
8625     if ( SMDS_MeshElement* newElem = aMesh->AddPolyhedralVolume( poly_nodes, quantities ))
8626     {
8627       aMesh->SetMeshElementOnShape( newElem, elem->getshapeId() );
8628       myLastCreatedElems.push_back( newElem );
8629       ReplaceElemInGroups( elem, newElem, aMesh );
8630     }
8631     aMesh->RemoveElement( elem );
8632   }
8633 }
8634
8635 namespace
8636 {
8637   //================================================================================
8638   /*!
8639    * \brief Transform any volume into data of SMDSEntity_Polyhedra
8640    */
8641   //================================================================================
8642
8643   void volumeToPolyhedron( const SMDS_MeshElement*         elem,
8644                            vector<const SMDS_MeshNode *> & nodes,
8645                            vector<int> &                   nbNodeInFaces )
8646   {
8647     nodes.clear();
8648     nbNodeInFaces.clear();
8649     SMDS_VolumeTool vTool ( elem );
8650     for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
8651     {
8652       const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
8653       nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
8654       nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
8655     }
8656   }
8657 }
8658
8659 //=======================================================================
8660 /*!
8661  * \brief Convert elements contained in a sub-mesh to quadratic
8662  * \return int - nb of checked elements
8663  */
8664 //=======================================================================
8665
8666 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh *   theSm,
8667                                              SMESH_MesherHelper& theHelper,
8668                                              const bool          theForce3d)
8669 {
8670   //MESSAGE("convertElemToQuadratic");
8671   int nbElem = 0;
8672   if( !theSm ) return nbElem;
8673
8674   vector<int> nbNodeInFaces;
8675   vector<const SMDS_MeshNode *> nodes;
8676   SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
8677   while(ElemItr->more())
8678   {
8679     nbElem++;
8680     const SMDS_MeshElement* elem = ElemItr->next();
8681     if( !elem ) continue;
8682
8683     // analyse a necessity of conversion
8684     const SMDSAbs_ElementType aType = elem->GetType();
8685     if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
8686       continue;
8687     const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
8688     bool hasCentralNodes = false;
8689     if ( elem->IsQuadratic() )
8690     {
8691       bool alreadyOK;
8692       switch ( aGeomType ) {
8693       case SMDSEntity_Quad_Triangle:
8694       case SMDSEntity_Quad_Quadrangle:
8695       case SMDSEntity_Quad_Hexa:
8696       case SMDSEntity_Quad_Penta:
8697         alreadyOK = !theHelper.GetIsBiQuadratic(); break;
8698
8699       case SMDSEntity_BiQuad_Triangle:
8700       case SMDSEntity_BiQuad_Quadrangle:
8701       case SMDSEntity_TriQuad_Hexa:
8702       case SMDSEntity_BiQuad_Penta:
8703         alreadyOK = theHelper.GetIsBiQuadratic();
8704         hasCentralNodes = true;
8705         break;
8706       default:
8707         alreadyOK = true;
8708       }
8709       // take into account already present medium nodes
8710       switch ( aType ) {
8711       case SMDSAbs_Volume:
8712         theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
8713       case SMDSAbs_Face:
8714         theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
8715       case SMDSAbs_Edge:
8716         theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
8717       default:;
8718       }
8719       if ( alreadyOK )
8720         continue;
8721     }
8722     // get elem data needed to re-create it
8723     //
8724     const int id      = elem->GetID();
8725     const int nbNodes = elem->NbCornerNodes();
8726     nodes.assign(elem->begin_nodes(), elem->end_nodes());
8727     if ( aGeomType == SMDSEntity_Polyhedra )
8728       nbNodeInFaces = static_cast<const SMDS_MeshVolume* >( elem )->GetQuantities();
8729     else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
8730       volumeToPolyhedron( elem, nodes, nbNodeInFaces );
8731
8732     // remove a linear element
8733     GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
8734
8735     // remove central nodes of biquadratic elements (biquad->quad conversion)
8736     if ( hasCentralNodes )
8737       for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
8738         if ( nodes[i]->NbInverseElements() == 0 )
8739           GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
8740
8741     const SMDS_MeshElement* NewElem = 0;
8742
8743     switch( aType )
8744     {
8745     case SMDSAbs_Edge :
8746     {
8747       NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
8748       break;
8749     }
8750     case SMDSAbs_Face :
8751     {
8752       switch(nbNodes)
8753       {
8754       case 3:
8755         NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
8756         break;
8757       case 4:
8758         NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8759         break;
8760       default:
8761         NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
8762       }
8763       break;
8764     }
8765     case SMDSAbs_Volume :
8766     {
8767       switch( aGeomType )
8768       {
8769       case SMDSEntity_Tetra:
8770         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8771         break;
8772       case SMDSEntity_Pyramid:
8773         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
8774         break;
8775       case SMDSEntity_Penta:
8776       case SMDSEntity_Quad_Penta:
8777       case SMDSEntity_BiQuad_Penta:
8778         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
8779         break;
8780       case SMDSEntity_Hexa:
8781       case SMDSEntity_Quad_Hexa:
8782       case SMDSEntity_TriQuad_Hexa:
8783         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
8784                                       nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
8785         break;
8786       case SMDSEntity_Hexagonal_Prism:
8787       default:
8788         NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
8789       }
8790       break;
8791     }
8792     default :
8793       continue;
8794     }
8795     ReplaceElemInGroups( elem, NewElem, GetMeshDS());
8796     if( NewElem && NewElem->getshapeId() < 1 )
8797       theSm->AddElement( NewElem );
8798   }
8799   return nbElem;
8800 }
8801 //=======================================================================
8802 //function : ConvertToQuadratic
8803 //purpose  :
8804 //=======================================================================
8805
8806 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
8807 {
8808   //MESSAGE("ConvertToQuadratic "<< theForce3d << " " << theToBiQuad);
8809   SMESHDS_Mesh* meshDS = GetMeshDS();
8810
8811   SMESH_MesherHelper aHelper(*myMesh);
8812
8813   aHelper.SetIsQuadratic( true );
8814   aHelper.SetIsBiQuadratic( theToBiQuad );
8815   aHelper.SetElementsOnShape(true);
8816   aHelper.ToFixNodeParameters( true );
8817
8818   // convert elements assigned to sub-meshes
8819   int nbCheckedElems = 0;
8820   if ( myMesh->HasShapeToMesh() )
8821   {
8822     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
8823     {
8824       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
8825       while ( smIt->more() ) {
8826         SMESH_subMesh* sm = smIt->next();
8827         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
8828           aHelper.SetSubShape( sm->GetSubShape() );
8829           nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
8830         }
8831       }
8832     }
8833   }
8834
8835   // convert elements NOT assigned to sub-meshes
8836   int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
8837   if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
8838   {
8839     aHelper.SetElementsOnShape(false);
8840     SMESHDS_SubMesh *smDS = 0;
8841
8842     // convert edges
8843     SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
8844     while( aEdgeItr->more() )
8845     {
8846       const SMDS_MeshEdge* edge = aEdgeItr->next();
8847       if ( !edge->IsQuadratic() )
8848       {
8849         int                  id = edge->GetID();
8850         const SMDS_MeshNode* n1 = edge->GetNode(0);
8851         const SMDS_MeshNode* n2 = edge->GetNode(1);
8852
8853         meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
8854
8855         const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
8856         ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
8857       }
8858       else
8859       {
8860         aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
8861       }
8862     }
8863
8864     // convert faces
8865     SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
8866     while( aFaceItr->more() )
8867     {
8868       const SMDS_MeshFace* face = aFaceItr->next();
8869       if ( !face ) continue;
8870       
8871       const SMDSAbs_EntityType type = face->GetEntityType();
8872       bool alreadyOK;
8873       switch( type )
8874       {
8875       case SMDSEntity_Quad_Triangle:
8876       case SMDSEntity_Quad_Quadrangle:
8877         alreadyOK = !theToBiQuad;
8878         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
8879         break;
8880       case SMDSEntity_BiQuad_Triangle:
8881       case SMDSEntity_BiQuad_Quadrangle:
8882         alreadyOK = theToBiQuad;
8883         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
8884         break;
8885       default: alreadyOK = false;
8886       }
8887       if ( alreadyOK )
8888         continue;
8889
8890       const int id = face->GetID();
8891       vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
8892
8893       meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
8894
8895       SMDS_MeshFace * NewFace = 0;
8896       switch( type )
8897       {
8898       case SMDSEntity_Triangle:
8899       case SMDSEntity_Quad_Triangle:
8900       case SMDSEntity_BiQuad_Triangle:
8901         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
8902         if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
8903           GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
8904         break;
8905
8906       case SMDSEntity_Quadrangle:
8907       case SMDSEntity_Quad_Quadrangle:
8908       case SMDSEntity_BiQuad_Quadrangle:
8909         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8910         if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
8911           GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
8912         break;
8913
8914       default:;
8915         NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
8916       }
8917       ReplaceElemInGroups( face, NewFace, GetMeshDS());
8918     }
8919
8920     // convert volumes
8921     vector<int> nbNodeInFaces;
8922     SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
8923     while(aVolumeItr->more())
8924     {
8925       const SMDS_MeshVolume* volume = aVolumeItr->next();
8926       if ( !volume ) continue;
8927
8928       const SMDSAbs_EntityType type = volume->GetEntityType();
8929       if ( volume->IsQuadratic() )
8930       {
8931         bool alreadyOK;
8932         switch ( type )
8933         {
8934         case SMDSEntity_Quad_Hexa:    alreadyOK = !theToBiQuad; break;
8935         case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
8936         case SMDSEntity_Quad_Penta:   alreadyOK = !theToBiQuad; break;
8937         case SMDSEntity_BiQuad_Penta: alreadyOK = theToBiQuad; break;
8938         default:                      alreadyOK = true;
8939         }
8940         if ( alreadyOK )
8941         {
8942           aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
8943           continue;
8944         }
8945       }
8946       const int id = volume->GetID();
8947       vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
8948       if ( type == SMDSEntity_Polyhedra )
8949         nbNodeInFaces = static_cast<const SMDS_MeshVolume* >(volume)->GetQuantities();
8950       else if ( type == SMDSEntity_Hexagonal_Prism )
8951         volumeToPolyhedron( volume, nodes, nbNodeInFaces );
8952
8953       meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
8954
8955       SMDS_MeshVolume * NewVolume = 0;
8956       switch ( type )
8957       {
8958       case SMDSEntity_Tetra:
8959         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
8960         break;
8961       case SMDSEntity_Hexa:
8962       case SMDSEntity_Quad_Hexa:
8963       case SMDSEntity_TriQuad_Hexa:
8964         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
8965                                       nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
8966         for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
8967           if ( nodes[i]->NbInverseElements() == 0 )
8968             GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
8969         break;
8970       case SMDSEntity_Pyramid:
8971         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
8972                                       nodes[3], nodes[4], id, theForce3d);
8973         break;
8974       case SMDSEntity_Penta:
8975       case SMDSEntity_Quad_Penta:
8976       case SMDSEntity_BiQuad_Penta:
8977         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
8978                                       nodes[3], nodes[4], nodes[5], id, theForce3d);
8979         for ( size_t i = 15; i < nodes.size(); ++i ) // rm central nodes
8980           if ( nodes[i]->NbInverseElements() == 0 )
8981             GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
8982         break;
8983       case SMDSEntity_Hexagonal_Prism:
8984       default:
8985         NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
8986       }
8987       ReplaceElemInGroups(volume, NewVolume, meshDS);
8988     }
8989   }
8990
8991   if ( !theForce3d )
8992   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
8993     // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
8994     // aHelper.FixQuadraticElements(myError);
8995     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
8996   }
8997 }
8998
8999 //================================================================================
9000 /*!
9001  * \brief Makes given elements quadratic
9002  *  \param theForce3d - if true, the medium nodes will be placed in the middle of link
9003  *  \param theElements - elements to make quadratic
9004  */
9005 //================================================================================
9006
9007 void SMESH_MeshEditor::ConvertToQuadratic(const bool        theForce3d,
9008                                           TIDSortedElemSet& theElements,
9009                                           const bool        theToBiQuad)
9010 {
9011   if ( theElements.empty() ) return;
9012
9013   // we believe that all theElements are of the same type
9014   const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9015
9016   // get all nodes shared by theElements
9017   TIDSortedNodeSet allNodes;
9018   TIDSortedElemSet::iterator eIt = theElements.begin();
9019   for ( ; eIt != theElements.end(); ++eIt )
9020     allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9021
9022   // complete theElements with elements of lower dim whose all nodes are in allNodes
9023
9024   TIDSortedElemSet quadAdjacentElems    [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9025   TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9026   TIDSortedNodeSet::iterator nIt = allNodes.begin();
9027   for ( ; nIt != allNodes.end(); ++nIt )
9028   {
9029     const SMDS_MeshNode* n = *nIt;
9030     SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9031     while ( invIt->more() )
9032     {
9033       const SMDS_MeshElement*      e = invIt->next();
9034       const SMDSAbs_ElementType type = e->GetType();
9035       if ( e->IsQuadratic() )
9036       {
9037         quadAdjacentElems[ type ].insert( e );
9038
9039         bool alreadyOK;
9040         switch ( e->GetEntityType() ) {
9041         case SMDSEntity_Quad_Triangle:
9042         case SMDSEntity_Quad_Quadrangle:
9043         case SMDSEntity_Quad_Hexa:         alreadyOK = !theToBiQuad; break;
9044         case SMDSEntity_BiQuad_Triangle:
9045         case SMDSEntity_BiQuad_Quadrangle:
9046         case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; break;
9047         default:                           alreadyOK = true;
9048         }
9049         if ( alreadyOK )
9050           continue;
9051       }
9052       if ( type >= elemType )
9053         continue; // same type or more complex linear element
9054
9055       if ( !checkedAdjacentElems[ type ].insert( e ).second )
9056         continue; // e is already checked
9057
9058       // check nodes
9059       bool allIn = true;
9060       SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9061       while ( nodeIt->more() && allIn )
9062         allIn = allNodes.count( nodeIt->next() );
9063       if ( allIn )
9064         theElements.insert(e );
9065     }
9066   }
9067
9068   SMESH_MesherHelper helper(*myMesh);
9069   helper.SetIsQuadratic( true );
9070   helper.SetIsBiQuadratic( theToBiQuad );
9071
9072   // add links of quadratic adjacent elements to the helper
9073
9074   if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9075     for ( eIt  = quadAdjacentElems[SMDSAbs_Edge].begin();
9076           eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9077     {
9078       helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9079     }
9080   if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9081     for ( eIt  = quadAdjacentElems[SMDSAbs_Face].begin();
9082           eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9083     {
9084       helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9085     }
9086   if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9087     for ( eIt  = quadAdjacentElems[SMDSAbs_Volume].begin();
9088           eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9089     {
9090       helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9091     }
9092
9093   // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9094
9095   SMESHDS_Mesh*  meshDS = GetMeshDS();
9096   SMESHDS_SubMesh* smDS = 0;
9097   for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9098   {
9099     const SMDS_MeshElement* elem = *eIt;
9100
9101     bool alreadyOK;
9102     int nbCentralNodes = 0;
9103     switch ( elem->GetEntityType() ) {
9104       // linear convertible
9105     case SMDSEntity_Edge:
9106     case SMDSEntity_Triangle:
9107     case SMDSEntity_Quadrangle:
9108     case SMDSEntity_Tetra:
9109     case SMDSEntity_Pyramid:
9110     case SMDSEntity_Hexa:
9111     case SMDSEntity_Penta:             alreadyOK = false;       nbCentralNodes = 0; break;
9112       // quadratic that can become bi-quadratic
9113     case SMDSEntity_Quad_Triangle:
9114     case SMDSEntity_Quad_Quadrangle:
9115     case SMDSEntity_Quad_Hexa:         alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9116       // bi-quadratic
9117     case SMDSEntity_BiQuad_Triangle:
9118     case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9119     case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9120       // the rest
9121     default:                           alreadyOK = true;
9122     }
9123     if ( alreadyOK ) continue;
9124
9125     const SMDSAbs_ElementType type = elem->GetType();
9126     const int                   id = elem->GetID();
9127     const int              nbNodes = elem->NbCornerNodes();
9128     vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9129
9130     helper.SetSubShape( elem->getshapeId() );
9131
9132     if ( !smDS || !smDS->Contains( elem ))
9133       smDS = meshDS->MeshElements( elem->getshapeId() );
9134     meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9135
9136     SMDS_MeshElement * newElem = 0;
9137     switch( nbNodes )
9138     {
9139     case 4: // cases for most frequently used element types go first (for optimization)
9140       if ( type == SMDSAbs_Volume )
9141         newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9142       else
9143         newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9144       break;
9145     case 8:
9146       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9147                                  nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9148       break;
9149     case 3:
9150       newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], id, theForce3d);
9151       break;
9152     case 2:
9153       newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9154       break;
9155     case 5:
9156       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9157                                  nodes[4], id, theForce3d);
9158       break;
9159     case 6:
9160       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9161                                  nodes[4], nodes[5], id, theForce3d);
9162       break;
9163     default:;
9164     }
9165     ReplaceElemInGroups( elem, newElem, meshDS);
9166     if( newElem && smDS )
9167       smDS->AddElement( newElem );
9168
9169     // remove central nodes
9170     for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9171       if ( nodes[i]->NbInverseElements() == 0 )
9172         meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9173
9174   } // loop on theElements
9175
9176   if ( !theForce3d )
9177   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9178     // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9179     // helper.FixQuadraticElements( myError );
9180     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9181   }
9182 }
9183
9184 //=======================================================================
9185 /*!
9186  * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9187  * \return int - nb of checked elements
9188  */
9189 //=======================================================================
9190
9191 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh *    theSm,
9192                                      SMDS_ElemIteratorPtr theItr,
9193                                      const int            theShapeID)
9194 {
9195   int nbElem = 0;
9196   SMESHDS_Mesh* meshDS = GetMeshDS();
9197   ElemFeatures elemType;
9198   vector<const SMDS_MeshNode *> nodes;
9199
9200   while( theItr->more() )
9201   {
9202     const SMDS_MeshElement* elem = theItr->next();
9203     nbElem++;
9204     if( elem && elem->IsQuadratic())
9205     {
9206       // get elem data
9207       int nbCornerNodes = elem->NbCornerNodes();
9208       nodes.assign( elem->begin_nodes(), elem->end_nodes() );
9209
9210       elemType.Init( elem, /*basicOnly=*/false ).SetID( elem->GetID() ).SetQuad( false );
9211
9212       //remove a quadratic element
9213       if ( !theSm || !theSm->Contains( elem ))
9214         theSm = meshDS->MeshElements( elem->getshapeId() );
9215       meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9216
9217       // remove medium nodes
9218       for ( size_t i = nbCornerNodes; i < nodes.size(); ++i )
9219         if ( nodes[i]->NbInverseElements() == 0 )
9220           meshDS->RemoveFreeNode( nodes[i], theSm );
9221
9222       // add a linear element
9223       nodes.resize( nbCornerNodes );
9224       SMDS_MeshElement * newElem = AddElement( nodes, elemType );
9225       ReplaceElemInGroups(elem, newElem, meshDS);
9226       if( theSm && newElem )
9227         theSm->AddElement( newElem );
9228     }
9229   }
9230   return nbElem;
9231 }
9232
9233 //=======================================================================
9234 //function : ConvertFromQuadratic
9235 //purpose  :
9236 //=======================================================================
9237
9238 bool SMESH_MeshEditor::ConvertFromQuadratic()
9239 {
9240   int nbCheckedElems = 0;
9241   if ( myMesh->HasShapeToMesh() )
9242   {
9243     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9244     {
9245       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9246       while ( smIt->more() ) {
9247         SMESH_subMesh* sm = smIt->next();
9248         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9249           nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9250       }
9251     }
9252   }
9253
9254   int totalNbElems =
9255     GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9256   if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9257   {
9258     SMESHDS_SubMesh *aSM = 0;
9259     removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9260   }
9261
9262   return true;
9263 }
9264
9265 namespace
9266 {
9267   //================================================================================
9268   /*!
9269    * \brief Return true if all medium nodes of the element are in the node set
9270    */
9271   //================================================================================
9272
9273   bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9274   {
9275     for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9276       if ( !nodeSet.count( elem->GetNode(i) ))
9277         return false;
9278     return true;
9279   }
9280 }
9281
9282 //================================================================================
9283 /*!
9284  * \brief Makes given elements linear
9285  */
9286 //================================================================================
9287
9288 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9289 {
9290   if ( theElements.empty() ) return;
9291
9292   // collect IDs of medium nodes of theElements; some of these nodes will be removed
9293   set<int> mediumNodeIDs;
9294   TIDSortedElemSet::iterator eIt = theElements.begin();
9295   for ( ; eIt != theElements.end(); ++eIt )
9296   {
9297     const SMDS_MeshElement* e = *eIt;
9298     for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9299       mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9300   }
9301
9302   // replace given elements by linear ones
9303   SMDS_ElemIteratorPtr elemIt = SMESHUtils::elemSetIterator( theElements );
9304   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9305
9306   // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9307   // except those elements sharing medium nodes of quadratic element whose medium nodes
9308   // are not all in mediumNodeIDs
9309
9310   // get remaining medium nodes
9311   TIDSortedNodeSet mediumNodes;
9312   set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9313   for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9314     if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9315       mediumNodes.insert( mediumNodes.end(), n );
9316
9317   // find more quadratic elements to convert
9318   TIDSortedElemSet moreElemsToConvert;
9319   TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9320   for ( ; nIt != mediumNodes.end(); ++nIt )
9321   {
9322     SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9323     while ( invIt->more() )
9324     {
9325       const SMDS_MeshElement* e = invIt->next();
9326       if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9327       {
9328         // find a more complex element including e and
9329         // whose medium nodes are not in mediumNodes
9330         bool complexFound = false;
9331         for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9332         {
9333           SMDS_ElemIteratorPtr invIt2 =
9334             (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9335           while ( invIt2->more() )
9336           {
9337             const SMDS_MeshElement* eComplex = invIt2->next();
9338             if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9339             {
9340               int nbCommonNodes = SMESH_MeshAlgos::GetCommonNodes( e, eComplex ).size();
9341               if ( nbCommonNodes == e->NbNodes())
9342               {
9343                 complexFound = true;
9344                 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9345                 break;
9346               }
9347             }
9348           }
9349         }
9350         if ( !complexFound )
9351           moreElemsToConvert.insert( e );
9352       }
9353     }
9354   }
9355   elemIt = SMESHUtils::elemSetIterator( moreElemsToConvert );
9356   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9357 }
9358
9359 //=======================================================================
9360 //function : SewSideElements
9361 //purpose  :
9362 //=======================================================================
9363
9364 SMESH_MeshEditor::Sew_Error
9365 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet&    theSide1,
9366                                    TIDSortedElemSet&    theSide2,
9367                                    const SMDS_MeshNode* theFirstNode1,
9368                                    const SMDS_MeshNode* theFirstNode2,
9369                                    const SMDS_MeshNode* theSecondNode1,
9370                                    const SMDS_MeshNode* theSecondNode2)
9371 {
9372   ClearLastCreated();
9373
9374   if ( theSide1.size() != theSide2.size() )
9375     return SEW_DIFF_NB_OF_ELEMENTS;
9376
9377   Sew_Error aResult = SEW_OK;
9378   // Algo:
9379   // 1. Build set of faces representing each side
9380   // 2. Find which nodes of the side 1 to merge with ones on the side 2
9381   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9382
9383   // =======================================================================
9384   // 1. Build set of faces representing each side:
9385   // =======================================================================
9386   // a. build set of nodes belonging to faces
9387   // b. complete set of faces: find missing faces whose nodes are in set of nodes
9388   // c. create temporary faces representing side of volumes if correspondent
9389   //    face does not exist
9390
9391   SMESHDS_Mesh* aMesh = GetMeshDS();
9392   // TODO algorithm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9393   //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9394   TIDSortedElemSet             faceSet1, faceSet2;
9395   set<const SMDS_MeshElement*> volSet1,  volSet2;
9396   set<const SMDS_MeshNode*>    nodeSet1, nodeSet2;
9397   TIDSortedElemSet             * faceSetPtr[] = { &faceSet1, &faceSet2 };
9398   set<const SMDS_MeshElement*> *  volSetPtr[] = { &volSet1,  &volSet2  };
9399   set<const SMDS_MeshNode*>    * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9400   TIDSortedElemSet             * elemSetPtr[] = { &theSide1, &theSide2 };
9401   int iSide, iFace, iNode;
9402
9403   list<const SMDS_MeshElement* > tempFaceList;
9404   for ( iSide = 0; iSide < 2; iSide++ ) {
9405     set<const SMDS_MeshNode*>    * nodeSet = nodeSetPtr[ iSide ];
9406     TIDSortedElemSet             * elemSet = elemSetPtr[ iSide ];
9407     TIDSortedElemSet             * faceSet = faceSetPtr[ iSide ];
9408     set<const SMDS_MeshElement*> * volSet  = volSetPtr [ iSide ];
9409     set<const SMDS_MeshElement*>::iterator vIt;
9410     TIDSortedElemSet::iterator eIt;
9411     set<const SMDS_MeshNode*>::iterator    nIt;
9412
9413     // check that given nodes belong to given elements
9414     const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9415     const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9416     int firstIndex = -1, secondIndex = -1;
9417     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9418       const SMDS_MeshElement* elem = *eIt;
9419       if ( firstIndex  < 0 ) firstIndex  = elem->GetNodeIndex( n1 );
9420       if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9421       if ( firstIndex > -1 && secondIndex > -1 ) break;
9422     }
9423     if ( firstIndex < 0 || secondIndex < 0 ) {
9424       // we can simply return until temporary faces created
9425       return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9426     }
9427
9428     // -----------------------------------------------------------
9429     // 1a. Collect nodes of existing faces
9430     //     and build set of face nodes in order to detect missing
9431     //     faces corresponding to sides of volumes
9432     // -----------------------------------------------------------
9433
9434     set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9435
9436     // loop on the given element of a side
9437     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9438       //const SMDS_MeshElement* elem = *eIt;
9439       const SMDS_MeshElement* elem = *eIt;
9440       if ( elem->GetType() == SMDSAbs_Face ) {
9441         faceSet->insert( elem );
9442         set <const SMDS_MeshNode*> faceNodeSet;
9443         SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9444         while ( nodeIt->more() ) {
9445           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9446           nodeSet->insert( n );
9447           faceNodeSet.insert( n );
9448         }
9449         setOfFaceNodeSet.insert( faceNodeSet );
9450       }
9451       else if ( elem->GetType() == SMDSAbs_Volume )
9452         volSet->insert( elem );
9453     }
9454     // ------------------------------------------------------------------------------
9455     // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9456     // ------------------------------------------------------------------------------
9457
9458     for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9459       SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9460       while ( fIt->more() ) { // loop on faces sharing a node
9461         const SMDS_MeshElement* f = fIt->next();
9462         if ( faceSet->find( f ) == faceSet->end() ) {
9463           // check if all nodes are in nodeSet and
9464           // complete setOfFaceNodeSet if they are
9465           set <const SMDS_MeshNode*> faceNodeSet;
9466           SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9467           bool allInSet = true;
9468           while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9469             const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9470             if ( nodeSet->find( n ) == nodeSet->end() )
9471               allInSet = false;
9472             else
9473               faceNodeSet.insert( n );
9474           }
9475           if ( allInSet ) {
9476             faceSet->insert( f );
9477             setOfFaceNodeSet.insert( faceNodeSet );
9478           }
9479         }
9480       }
9481     }
9482
9483     // -------------------------------------------------------------------------
9484     // 1c. Create temporary faces representing sides of volumes if correspondent
9485     //     face does not exist
9486     // -------------------------------------------------------------------------
9487
9488     if ( !volSet->empty() ) {
9489       //int nodeSetSize = nodeSet->size();
9490
9491       // loop on given volumes
9492       for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
9493         SMDS_VolumeTool vol (*vIt);
9494         // loop on volume faces: find free faces
9495         // --------------------------------------
9496         list<const SMDS_MeshElement* > freeFaceList;
9497         for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
9498           if ( !vol.IsFreeFace( iFace ))
9499             continue;
9500           // check if there is already a face with same nodes in a face set
9501           const SMDS_MeshElement* aFreeFace = 0;
9502           const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
9503           int nbNodes = vol.NbFaceNodes( iFace );
9504           set <const SMDS_MeshNode*> faceNodeSet;
9505           vol.GetFaceNodes( iFace, faceNodeSet );
9506           bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
9507           if ( isNewFace ) {
9508             // no such a face is given but it still can exist, check it
9509             vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
9510             aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
9511           }
9512           if ( !aFreeFace ) {
9513             // create a temporary face
9514             if ( nbNodes == 3 ) {
9515               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
9516               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
9517             }
9518             else if ( nbNodes == 4 ) {
9519               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9520               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9521             }
9522             else {
9523               vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9524               //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
9525               aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
9526             }
9527             if ( aFreeFace )
9528               tempFaceList.push_back( aFreeFace );
9529           }
9530
9531           if ( aFreeFace )
9532             freeFaceList.push_back( aFreeFace );
9533
9534         } // loop on faces of a volume
9535
9536         // choose one of several free faces of a volume
9537         // --------------------------------------------
9538         if ( freeFaceList.size() > 1 ) {
9539           // choose a face having max nb of nodes shared by other elems of a side
9540           int maxNbNodes = -1;
9541           list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
9542           while ( fIt != freeFaceList.end() ) { // loop on free faces
9543             int nbSharedNodes = 0;
9544             SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9545             while ( nodeIt->more() ) { // loop on free face nodes
9546               const SMDS_MeshNode* n =
9547                 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9548               SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
9549               while ( invElemIt->more() ) {
9550                 const SMDS_MeshElement* e = invElemIt->next();
9551                 nbSharedNodes += faceSet->count( e );
9552                 nbSharedNodes += elemSet->count( e );
9553               }
9554             }
9555             if ( nbSharedNodes > maxNbNodes ) {
9556               maxNbNodes = nbSharedNodes;
9557               freeFaceList.erase( freeFaceList.begin(), fIt++ );
9558             }
9559             else if ( nbSharedNodes == maxNbNodes ) {
9560               fIt++;
9561             }
9562             else {
9563               freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
9564             }
9565           }
9566           if ( freeFaceList.size() > 1 )
9567           {
9568             // could not choose one face, use another way
9569             // choose a face most close to the bary center of the opposite side
9570             gp_XYZ aBC( 0., 0., 0. );
9571             set <const SMDS_MeshNode*> addedNodes;
9572             TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
9573             eIt = elemSet2->begin();
9574             for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
9575               SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
9576               while ( nodeIt->more() ) { // loop on free face nodes
9577                 const SMDS_MeshNode* n =
9578                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9579                 if ( addedNodes.insert( n ).second )
9580                   aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
9581               }
9582             }
9583             aBC /= addedNodes.size();
9584             double minDist = DBL_MAX;
9585             fIt = freeFaceList.begin();
9586             while ( fIt != freeFaceList.end() ) { // loop on free faces
9587               double dist = 0;
9588               SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9589               while ( nodeIt->more() ) { // loop on free face nodes
9590                 const SMDS_MeshNode* n =
9591                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9592                 gp_XYZ p( n->X(),n->Y(),n->Z() );
9593                 dist += ( aBC - p ).SquareModulus();
9594               }
9595               if ( dist < minDist ) {
9596                 minDist = dist;
9597                 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9598               }
9599               else
9600                 fIt = freeFaceList.erase( fIt++ );
9601             }
9602           }
9603         } // choose one of several free faces of a volume
9604
9605         if ( freeFaceList.size() == 1 ) {
9606           const SMDS_MeshElement* aFreeFace = freeFaceList.front();
9607           faceSet->insert( aFreeFace );
9608           // complete a node set with nodes of a found free face
9609           //           for ( iNode = 0; iNode < ; iNode++ )
9610           //             nodeSet->insert( fNodes[ iNode ] );
9611         }
9612
9613       } // loop on volumes of a side
9614
9615       //       // complete a set of faces if new nodes in a nodeSet appeared
9616       //       // ----------------------------------------------------------
9617       //       if ( nodeSetSize != nodeSet->size() ) {
9618       //         for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9619       //           SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9620       //           while ( fIt->more() ) { // loop on faces sharing a node
9621       //             const SMDS_MeshElement* f = fIt->next();
9622       //             if ( faceSet->find( f ) == faceSet->end() ) {
9623       //               // check if all nodes are in nodeSet and
9624       //               // complete setOfFaceNodeSet if they are
9625       //               set <const SMDS_MeshNode*> faceNodeSet;
9626       //               SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9627       //               bool allInSet = true;
9628       //               while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9629       //                 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9630       //                 if ( nodeSet->find( n ) == nodeSet->end() )
9631       //                   allInSet = false;
9632       //                 else
9633       //                   faceNodeSet.insert( n );
9634       //               }
9635       //               if ( allInSet ) {
9636       //                 faceSet->insert( f );
9637       //                 setOfFaceNodeSet.insert( faceNodeSet );
9638       //               }
9639       //             }
9640       //           }
9641       //         }
9642       //       }
9643     } // Create temporary faces, if there are volumes given
9644   } // loop on sides
9645
9646   if ( faceSet1.size() != faceSet2.size() ) {
9647     // delete temporary faces: they are in reverseElements of actual nodes
9648     //    SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9649     //    while ( tmpFaceIt->more() )
9650     //      aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9651     //    list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9652     //    for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9653     //      aMesh->RemoveElement(*tmpFaceIt);
9654     MESSAGE("Diff nb of faces");
9655     return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9656   }
9657
9658   // ============================================================
9659   // 2. Find nodes to merge:
9660   //              bind a node to remove to a node to put instead
9661   // ============================================================
9662
9663   TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
9664   if ( theFirstNode1 != theFirstNode2 )
9665     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9666   if ( theSecondNode1 != theSecondNode2 )
9667     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9668
9669   LinkID_Gen aLinkID_Gen( GetMeshDS() );
9670   set< long > linkIdSet; // links to process
9671   linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
9672
9673   typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
9674   list< NLink > linkList[2];
9675   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9676   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9677   // loop on links in linkList; find faces by links and append links
9678   // of the found faces to linkList
9679   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9680   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
9681   {
9682     NLink link[] = { *linkIt[0], *linkIt[1] };
9683     long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
9684     if ( !linkIdSet.count( linkID ) )
9685       continue;
9686
9687     // by links, find faces in the face sets,
9688     // and find indices of link nodes in the found faces;
9689     // in a face set, there is only one or no face sharing a link
9690     // ---------------------------------------------------------------
9691
9692     const SMDS_MeshElement* face[] = { 0, 0 };
9693     vector<const SMDS_MeshNode*> fnodes[2];
9694     int iLinkNode[2][2];
9695     TIDSortedElemSet avoidSet;
9696     for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9697       const SMDS_MeshNode* n1 = link[iSide].first;
9698       const SMDS_MeshNode* n2 = link[iSide].second;
9699       //cout << "Side " << iSide << " ";
9700       //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
9701       // find a face by two link nodes
9702       face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
9703                                                       *faceSetPtr[ iSide ], avoidSet,
9704                                                       &iLinkNode[iSide][0],
9705                                                       &iLinkNode[iSide][1] );
9706       if ( face[ iSide ])
9707       {
9708         //cout << " F " << face[ iSide]->GetID() <<endl;
9709         faceSetPtr[ iSide ]->erase( face[ iSide ]);
9710         // put face nodes to fnodes
9711         SMDS_MeshElement::iterator nIt( face[ iSide ]->interlacedNodesIterator() ), nEnd;
9712         fnodes[ iSide ].assign( nIt, nEnd );
9713         fnodes[ iSide ].push_back( fnodes[ iSide ].front());
9714       }
9715     }
9716
9717     // check similarity of elements of the sides
9718     if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
9719       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
9720       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
9721         aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9722       }
9723       else {
9724         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9725       }
9726       break; // do not return because it's necessary to remove tmp faces
9727     }
9728
9729     // set nodes to merge
9730     // -------------------
9731
9732     if ( face[0] && face[1] )  {
9733       const int nbNodes = face[0]->NbNodes();
9734       if ( nbNodes != face[1]->NbNodes() ) {
9735         MESSAGE("Diff nb of face nodes");
9736         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9737         break; // do not return because it s necessary to remove tmp faces
9738       }
9739       bool reverse[] = { false, false }; // order of nodes in the link
9740       for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9741         // analyse link orientation in faces
9742         int i1 = iLinkNode[ iSide ][ 0 ];
9743         int i2 = iLinkNode[ iSide ][ 1 ];
9744         reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
9745       }
9746       int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
9747       int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
9748       for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
9749       {
9750         nReplaceMap.insert  ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
9751                                           fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
9752       }
9753
9754       // add other links of the faces to linkList
9755       // -----------------------------------------
9756
9757       for ( iNode = 0; iNode < nbNodes; iNode++ )  {
9758         linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
9759         pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
9760         if ( !iter_isnew.second ) { // already in a set: no need to process
9761           linkIdSet.erase( iter_isnew.first );
9762         }
9763         else // new in set == encountered for the first time: add
9764         {
9765           const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
9766           const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
9767           linkList[0].push_back ( NLink( n1, n2 ));
9768           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
9769         }
9770       }
9771     } // 2 faces found
9772
9773     if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
9774       break;
9775
9776   } // loop on link lists
9777
9778   if ( aResult == SEW_OK &&
9779        ( //linkIt[0] != linkList[0].end() ||
9780         !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
9781     MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
9782              " " << (faceSetPtr[1]->empty()));
9783     aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9784   }
9785
9786   // ====================================================================
9787   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9788   // ====================================================================
9789
9790   // delete temporary faces
9791   //  SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9792   //  while ( tmpFaceIt->more() )
9793   //    aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9794   list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9795   for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9796     aMesh->RemoveElement(*tmpFaceIt);
9797
9798   if ( aResult != SEW_OK)
9799     return aResult;
9800
9801   list< int > nodeIDsToRemove;
9802   vector< const SMDS_MeshNode*> nodes;
9803   ElemFeatures elemType;
9804
9805   // loop on nodes replacement map
9806   TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
9807   for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
9808     if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second )
9809     {
9810       const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
9811       nodeIDsToRemove.push_back( nToRemove->GetID() );
9812       // loop on elements sharing nToRemove
9813       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
9814       while ( invElemIt->more() ) {
9815         const SMDS_MeshElement* e = invElemIt->next();
9816         // get a new suite of nodes: make replacement
9817         int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
9818         nodes.resize( nbNodes );
9819         SMDS_ElemIteratorPtr nIt = e->nodesIterator();
9820         while ( nIt->more() ) {
9821           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
9822           nnIt = nReplaceMap.find( n );
9823           if ( nnIt != nReplaceMap.end() ) {
9824             nbReplaced++;
9825             n = (*nnIt).second;
9826           }
9827           nodes[ i++ ] = n;
9828         }
9829         //       if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
9830         //         elemIDsToRemove.push_back( e->GetID() );
9831         //       else
9832         if ( nbReplaced )
9833         {
9834           elemType.Init( e, /*basicOnly=*/false ).SetID( e->GetID() );
9835           aMesh->RemoveElement( e );
9836
9837           if ( SMDS_MeshElement* newElem = this->AddElement( nodes, elemType ))
9838           {
9839             AddToSameGroups( newElem, e, aMesh );
9840             if ( int aShapeId = e->getshapeId() )
9841               aMesh->SetMeshElementOnShape( newElem, aShapeId );
9842           }
9843         }
9844       }
9845     }
9846
9847   Remove( nodeIDsToRemove, true );
9848
9849   return aResult;
9850 }
9851
9852 //================================================================================
9853 /*!
9854  * \brief Find corresponding nodes in two sets of faces
9855  * \param theSide1 - first face set
9856  * \param theSide2 - second first face
9857  * \param theFirstNode1 - a boundary node of set 1
9858  * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
9859  * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
9860  * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
9861  * \param nReplaceMap - output map of corresponding nodes
9862  * \return bool  - is a success or not
9863  */
9864 //================================================================================
9865
9866 #ifdef _DEBUG_
9867 //#define DEBUG_MATCHING_NODES
9868 #endif
9869
9870 SMESH_MeshEditor::Sew_Error
9871 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
9872                                     set<const SMDS_MeshElement*>& theSide2,
9873                                     const SMDS_MeshNode*          theFirstNode1,
9874                                     const SMDS_MeshNode*          theFirstNode2,
9875                                     const SMDS_MeshNode*          theSecondNode1,
9876                                     const SMDS_MeshNode*          theSecondNode2,
9877                                     TNodeNodeMap &                nReplaceMap)
9878 {
9879   set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
9880
9881   nReplaceMap.clear();
9882   //if ( theFirstNode1 != theFirstNode2 )
9883   nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9884   //if ( theSecondNode1 != theSecondNode2 )
9885   nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9886
9887   set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
9888   linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
9889
9890   list< NLink > linkList[2];
9891   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9892   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9893
9894   // loop on links in linkList; find faces by links and append links
9895   // of the found faces to linkList
9896   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9897   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
9898     NLink link[] = { *linkIt[0], *linkIt[1] };
9899     if ( linkSet.find( link[0] ) == linkSet.end() )
9900       continue;
9901
9902     // by links, find faces in the face sets,
9903     // and find indices of link nodes in the found faces;
9904     // in a face set, there is only one or no face sharing a link
9905     // ---------------------------------------------------------------
9906
9907     const SMDS_MeshElement* face[] = { 0, 0 };
9908     list<const SMDS_MeshNode*> notLinkNodes[2];
9909     //bool reverse[] = { false, false }; // order of notLinkNodes
9910     int nbNodes[2];
9911     for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
9912     {
9913       const SMDS_MeshNode* n1 = link[iSide].first;
9914       const SMDS_MeshNode* n2 = link[iSide].second;
9915       set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
9916       set< const SMDS_MeshElement* > facesOfNode1;
9917       for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
9918       {
9919         // during a loop of the first node, we find all faces around n1,
9920         // during a loop of the second node, we find one face sharing both n1 and n2
9921         const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
9922         SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
9923         while ( fIt->more() ) { // loop on faces sharing a node
9924           const SMDS_MeshElement* f = fIt->next();
9925           if (faceSet->find( f ) != faceSet->end() && // f is in face set
9926               ! facesOfNode1.insert( f ).second ) // f encounters twice
9927           {
9928             if ( face[ iSide ] ) {
9929               MESSAGE( "2 faces per link " );
9930               return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9931             }
9932             face[ iSide ] = f;
9933             faceSet->erase( f );
9934
9935             // get not link nodes
9936             int nbN = f->NbNodes();
9937             if ( f->IsQuadratic() )
9938               nbN /= 2;
9939             nbNodes[ iSide ] = nbN;
9940             list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
9941             int i1 = f->GetNodeIndex( n1 );
9942             int i2 = f->GetNodeIndex( n2 );
9943             int iEnd = nbN, iBeg = -1, iDelta = 1;
9944             bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
9945             if ( reverse ) {
9946               std::swap( iEnd, iBeg ); iDelta = -1;
9947             }
9948             int i = i2;
9949             while ( true ) {
9950               i += iDelta;
9951               if ( i == iEnd ) i = iBeg + iDelta;
9952               if ( i == i1 ) break;
9953               nodes.push_back ( f->GetNode( i ) );
9954             }
9955           }
9956         }
9957       }
9958     }
9959     // check similarity of elements of the sides
9960     if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
9961       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
9962       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
9963         return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9964       }
9965       else {
9966         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9967       }
9968     }
9969
9970     // set nodes to merge
9971     // -------------------
9972
9973     if ( face[0] && face[1] )  {
9974       if ( nbNodes[0] != nbNodes[1] ) {
9975         MESSAGE("Diff nb of face nodes");
9976         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9977       }
9978 #ifdef DEBUG_MATCHING_NODES
9979       MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
9980                 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
9981                 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
9982 #endif
9983       int nbN = nbNodes[0];
9984       {
9985         list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
9986         list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
9987         for ( int i = 0 ; i < nbN - 2; ++i ) {
9988 #ifdef DEBUG_MATCHING_NODES
9989           MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
9990 #endif
9991           nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
9992         }
9993       }
9994
9995       // add other links of the face 1 to linkList
9996       // -----------------------------------------
9997
9998       const SMDS_MeshElement* f0 = face[0];
9999       const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10000       for ( int i = 0; i < nbN; i++ )
10001       {
10002         const SMDS_MeshNode* n2 = f0->GetNode( i );
10003         pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10004           linkSet.insert( SMESH_TLink( n1, n2 ));
10005         if ( !iter_isnew.second ) { // already in a set: no need to process
10006           linkSet.erase( iter_isnew.first );
10007         }
10008         else // new in set == encountered for the first time: add
10009         {
10010 #ifdef DEBUG_MATCHING_NODES
10011           MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10012                     << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10013 #endif
10014           linkList[0].push_back ( NLink( n1, n2 ));
10015           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10016         }
10017         n1 = n2;
10018       }
10019     } // 2 faces found
10020   } // loop on link lists
10021
10022   return SEW_OK;
10023 }
10024
10025 namespace // automatically find theAffectedElems for DoubleNodes()
10026 {
10027   bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem );
10028
10029   //--------------------------------------------------------------------------------
10030   // Nodes shared by adjacent FissureBorder's.
10031   // 1 node  if FissureBorder separates faces
10032   // 2 nodes if FissureBorder separates volumes
10033   struct SubBorder
10034   {
10035     const SMDS_MeshNode* _nodes[2];
10036     int                  _nbNodes;
10037
10038     SubBorder( const SMDS_MeshNode* n1, const SMDS_MeshNode* n2 = 0 )
10039     {
10040       _nodes[0] = n1;
10041       _nodes[1] = n2;
10042       _nbNodes = bool( n1 ) + bool( n2 );
10043       if ( _nbNodes == 2 && n1 > n2 )
10044         std::swap( _nodes[0], _nodes[1] );
10045     }
10046     bool operator<( const SubBorder& other ) const
10047     {
10048       for ( int i = 0; i < _nbNodes; ++i )
10049       {
10050         if ( _nodes[i] < other._nodes[i] ) return true;
10051         if ( _nodes[i] > other._nodes[i] ) return false;
10052       }
10053       return false;
10054     }
10055   };
10056
10057   //--------------------------------------------------------------------------------
10058   // Map a SubBorder to all FissureBorder it bounds
10059   struct FissureBorder;
10060   typedef std::map< SubBorder, std::vector< FissureBorder* > > TBorderLinks;
10061   typedef TBorderLinks::iterator                               TMappedSub;
10062
10063   //--------------------------------------------------------------------------------
10064   /*!
10065    * \brief Element border (volume facet or face edge) at a fissure
10066    */
10067   struct FissureBorder
10068   {
10069     std::vector< const SMDS_MeshNode* > _nodes;    // border nodes
10070     const SMDS_MeshElement*             _elems[2]; // volume or face adjacent to fissure
10071
10072     std::vector< TMappedSub           > _mappedSubs;  // Sub() in TBorderLinks map
10073     std::vector< const SMDS_MeshNode* > _sortedNodes; // to compare FissureBorder's
10074
10075     FissureBorder( FissureBorder && from ) // move constructor
10076     {
10077       std::swap( _nodes,       from._nodes );
10078       std::swap( _sortedNodes, from._sortedNodes );
10079       _elems[0] = from._elems[0];
10080       _elems[1] = from._elems[1];
10081     }
10082
10083     FissureBorder( const SMDS_MeshElement*                  elemToDuplicate,
10084                    std::vector< const SMDS_MeshElement* > & adjElems)
10085       : _nodes( elemToDuplicate->NbCornerNodes() )
10086     {
10087       for ( size_t i = 0; i < _nodes.size(); ++i )
10088         _nodes[i] = elemToDuplicate->GetNode( i );
10089
10090       SMDSAbs_ElementType type = SMDSAbs_ElementType( elemToDuplicate->GetType() + 1 );
10091       findAdjacent( type, adjElems );
10092     }
10093
10094     FissureBorder( const SMDS_MeshNode**                    nodes,
10095                    const size_t                             nbNodes,
10096                    const SMDSAbs_ElementType                adjElemsType,
10097                    std::vector< const SMDS_MeshElement* > & adjElems)
10098       : _nodes( nodes, nodes + nbNodes )
10099     {
10100       findAdjacent( adjElemsType, adjElems );
10101     }
10102
10103     void findAdjacent( const SMDSAbs_ElementType                adjElemsType,
10104                        std::vector< const SMDS_MeshElement* > & adjElems)
10105     {
10106       _elems[0] = _elems[1] = 0;
10107       adjElems.clear();
10108       if ( SMDS_Mesh::GetElementsByNodes( _nodes, adjElems, adjElemsType ))
10109         for ( size_t i = 0; i < adjElems.size() && i < 2; ++i )
10110           _elems[i] = adjElems[i];
10111     }
10112
10113     bool operator<( const FissureBorder& other ) const
10114     {
10115       return GetSortedNodes() < other.GetSortedNodes();
10116     }
10117
10118     const std::vector< const SMDS_MeshNode* >& GetSortedNodes() const
10119     {
10120       if ( _sortedNodes.empty() && !_nodes.empty() )
10121       {
10122         FissureBorder* me = const_cast<FissureBorder*>( this );
10123         me->_sortedNodes = me->_nodes;
10124         std::sort( me->_sortedNodes.begin(), me->_sortedNodes.end() );
10125       }
10126       return _sortedNodes;
10127     }
10128
10129     size_t NbSub() const
10130     {
10131       return _nodes.size();
10132     }
10133
10134     SubBorder Sub(size_t i) const
10135     {
10136       return SubBorder( _nodes[i], NbSub() > 2 ? _nodes[ (i+1)%NbSub() ] : 0 );
10137     }
10138
10139     void AddSelfTo( TBorderLinks& borderLinks )
10140     {
10141       _mappedSubs.resize( NbSub() );
10142       for ( size_t i = 0; i < NbSub(); ++i )
10143       {
10144         TBorderLinks::iterator s2b =
10145           borderLinks.insert( std::make_pair( Sub(i), TBorderLinks::mapped_type() )).first;
10146         s2b->second.push_back( this );
10147         _mappedSubs[ i ] = s2b;
10148       }
10149     }
10150
10151     void Clear()
10152     {
10153       _nodes.clear();
10154     }
10155
10156     const SMDS_MeshElement* GetMarkedElem() const
10157     {
10158       if ( _nodes.empty() ) return 0; // cleared
10159       if ( _elems[0] && _elems[0]->isMarked() ) return _elems[0];
10160       if ( _elems[1] && _elems[1]->isMarked() ) return _elems[1];
10161       return 0;
10162     }
10163
10164     gp_XYZ GetNorm() const // normal to the border
10165     {
10166       gp_XYZ norm;
10167       if ( _nodes.size() == 2 )
10168       {
10169         gp_XYZ avgNorm( 0,0,0 ); // sum of normals of adjacent faces
10170         if ( SMESH_MeshAlgos::FaceNormal( _elems[0], norm ))
10171           avgNorm += norm;
10172         if ( SMESH_MeshAlgos::FaceNormal( _elems[1], norm ))
10173           avgNorm += norm;
10174
10175         gp_XYZ bordDir( SMESH_NodeXYZ( _nodes[0] ) - SMESH_NodeXYZ( _nodes[1] ));
10176         norm = bordDir ^ avgNorm;
10177       }
10178       else
10179       {
10180         SMESH_NodeXYZ p0( _nodes[0] );
10181         SMESH_NodeXYZ p1( _nodes[1] );
10182         SMESH_NodeXYZ p2( _nodes[2] );
10183         norm = ( p0 - p1 ) ^ ( p2 - p1 );
10184       }
10185       if ( isOut( _nodes[0], norm, GetMarkedElem() ))
10186         norm.Reverse();
10187
10188       return norm;
10189     }
10190
10191     void ChooseSide() // mark an _elem located at positive side of fissure
10192     {
10193       _elems[0]->setIsMarked( true );
10194       gp_XYZ norm = GetNorm();
10195       double maxX = norm.Coord(1);
10196       if ( Abs( maxX ) < Abs( norm.Coord(2)) ) maxX = norm.Coord(2);
10197       if ( Abs( maxX ) < Abs( norm.Coord(3)) ) maxX = norm.Coord(3);
10198       if ( maxX < 0 )
10199       {
10200         _elems[0]->setIsMarked( false );
10201         _elems[1]->setIsMarked( true );
10202       }
10203     }
10204
10205   }; // struct FissureBorder
10206
10207   //--------------------------------------------------------------------------------
10208   /*!
10209    * \brief Classifier of elements at fissure edge
10210    */
10211   class FissureNormal
10212   {
10213     std::vector< gp_XYZ > _normals;
10214     bool                  _bothIn;
10215
10216   public:
10217     void Add( const SMDS_MeshNode* n, const FissureBorder& bord )
10218     {
10219       _bothIn = false;
10220       _normals.reserve(2);
10221       _normals.push_back( bord.GetNorm() );
10222       if ( _normals.size() == 2 )
10223         _bothIn = !isOut( n, _normals[0], bord.GetMarkedElem() );
10224     }
10225
10226     bool IsIn( const SMDS_MeshNode* n, const SMDS_MeshElement* elem ) const
10227     {
10228       bool isIn = false;
10229       switch ( _normals.size() ) {
10230       case 1:
10231       {
10232         isIn = !isOut( n, _normals[0], elem );
10233         break;
10234       }
10235       case 2:
10236       {
10237         bool in1 = !isOut( n, _normals[0], elem );
10238         bool in2 = !isOut( n, _normals[1], elem );
10239         isIn = _bothIn ? ( in1 && in2 ) : ( in1 || in2 );
10240       }
10241       }
10242       return isIn;
10243     }
10244   };
10245
10246   //================================================================================
10247   /*!
10248    * \brief Classify an element by a plane passing through a node
10249    */
10250   //================================================================================
10251
10252   bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem )
10253   {
10254     SMESH_NodeXYZ p = n;
10255     double sumDot = 0;
10256     for ( int i = 0, nb = elem->NbCornerNodes(); i < nb; ++i )
10257     {
10258       SMESH_NodeXYZ pi = elem->GetNode( i );
10259       sumDot += norm * ( pi - p );
10260     }
10261     return sumDot < -1e-100;
10262   }
10263
10264   //================================================================================
10265   /*!
10266    * \brief Find FissureBorder's by nodes to duplicate
10267    */
10268   //================================================================================
10269
10270   void findFissureBorders( const TIDSortedElemSet&        theNodes,
10271                            std::vector< FissureBorder > & theFissureBorders )
10272   {
10273     TIDSortedElemSet::const_iterator nIt = theNodes.begin();
10274     const SMDS_MeshNode* n = dynamic_cast< const SMDS_MeshNode*>( *nIt );
10275     if ( !n ) return;
10276     SMDSAbs_ElementType elemType = SMDSAbs_Volume;
10277     if ( n->NbInverseElements( elemType ) == 0 )
10278     {
10279       elemType = SMDSAbs_Face;
10280       if ( n->NbInverseElements( elemType ) == 0 )
10281         return;
10282     }
10283     // unmark elements touching the fissure
10284     for ( ; nIt != theNodes.end(); ++nIt )
10285       SMESH_MeshAlgos::MarkElems( cast2Node(*nIt)->GetInverseElementIterator(), false );
10286
10287     // loop on elements touching the fissure to get their borders belonging to the fissure
10288     std::set< FissureBorder >              fissureBorders;
10289     std::vector< const SMDS_MeshElement* > adjElems;
10290     std::vector< const SMDS_MeshNode* >    nodes;
10291     SMDS_VolumeTool volTool;
10292     for ( nIt = theNodes.begin(); nIt != theNodes.end(); ++nIt )
10293     {
10294       SMDS_ElemIteratorPtr invIt = cast2Node(*nIt)->GetInverseElementIterator( elemType );
10295       while ( invIt->more() )
10296       {
10297         const SMDS_MeshElement* eInv = invIt->next();
10298         if ( eInv->isMarked() ) continue;
10299         eInv->setIsMarked( true );
10300
10301         if ( elemType == SMDSAbs_Volume )
10302         {
10303           volTool.Set( eInv );
10304           int iQuad = eInv->IsQuadratic() ? 2 : 1;
10305           for ( int iF = 0, nbF = volTool.NbFaces(); iF < nbF; ++iF )
10306           {
10307             const SMDS_MeshNode** nn = volTool.GetFaceNodes( iF );
10308             int                  nbN = volTool.NbFaceNodes( iF ) / iQuad;
10309             nodes.clear();
10310             bool allOnFissure = true;
10311             for ( int iN = 0; iN < nbN  && allOnFissure; iN += iQuad )
10312               if (( allOnFissure = theNodes.count( nn[ iN ])))
10313                 nodes.push_back( nn[ iN ]);
10314             if ( allOnFissure )
10315               fissureBorders.insert( std::move( FissureBorder( &nodes[0], nodes.size(),
10316                                                                elemType, adjElems )));
10317           }
10318         }
10319         else // elemType == SMDSAbs_Face
10320         {
10321           const SMDS_MeshNode* nn[2] = { eInv->GetNode( eInv->NbCornerNodes()-1 ), 0 };
10322           bool            onFissure0 = theNodes.count( nn[0] ), onFissure1;
10323           for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN; ++iN )
10324           {
10325             nn[1]      = eInv->GetNode( iN );
10326             onFissure1 = theNodes.count( nn[1] );
10327             if ( onFissure0 && onFissure1 )
10328               fissureBorders.insert( std::move( FissureBorder( nn, 2, elemType, adjElems )));
10329             nn[0]      = nn[1];
10330             onFissure0 = onFissure1;
10331           }
10332         }
10333       }
10334     }
10335
10336     theFissureBorders.reserve( theFissureBorders.size() + fissureBorders.size());
10337     std::set< FissureBorder >::iterator bord = fissureBorders.begin();
10338     for ( ; bord != fissureBorders.end(); ++bord )
10339     {
10340       theFissureBorders.push_back( std::move( const_cast<FissureBorder&>( *bord ) ));
10341     }
10342     return;
10343   } // findFissureBorders()
10344
10345   //================================================================================
10346   /*!
10347    * \brief Find elements on one side of a fissure defined by elements or nodes to duplicate
10348    *  \param [in] theElemsOrNodes - elements or nodes to duplicate
10349    *  \param [in] theNodesNot - nodes not to duplicate
10350    *  \param [out] theAffectedElems - the found elements
10351    */
10352   //================================================================================
10353
10354   void findAffectedElems( const TIDSortedElemSet& theElemsOrNodes,
10355                           TIDSortedElemSet&       theAffectedElems)
10356   {
10357     if ( theElemsOrNodes.empty() ) return;
10358
10359     // find FissureBorder's
10360
10361     std::vector< FissureBorder >           fissure;
10362     std::vector< const SMDS_MeshElement* > elemsByFacet;
10363
10364     TIDSortedElemSet::const_iterator elIt = theElemsOrNodes.begin();
10365     if ( (*elIt)->GetType() == SMDSAbs_Node )
10366     {
10367       findFissureBorders( theElemsOrNodes, fissure );
10368     }
10369     else
10370     {
10371       fissure.reserve( theElemsOrNodes.size() );
10372       for ( ; elIt != theElemsOrNodes.end(); ++elIt )
10373         fissure.push_back( std::move( FissureBorder( *elIt, elemsByFacet )));
10374     }
10375     if ( fissure.empty() )
10376       return;
10377
10378     // fill borderLinks
10379
10380     TBorderLinks borderLinks;
10381
10382     for ( size_t i = 0; i < fissure.size(); ++i )
10383     {
10384       fissure[i].AddSelfTo( borderLinks );
10385     }
10386
10387     // get theAffectedElems
10388
10389     // unmark elements having nodes on the fissure, theAffectedElems elements will be marked
10390     for ( size_t i = 0; i < fissure.size(); ++i )
10391       for ( size_t j = 0; j < fissure[i]._nodes.size(); ++j )
10392       {
10393         SMESH_MeshAlgos::MarkElemNodes( fissure[i]._nodes[j]->GetInverseElementIterator(),
10394                                         false, /*markElem=*/true );
10395       }
10396
10397     std::vector<const SMDS_MeshNode *>                 facetNodes;
10398     std::map< const SMDS_MeshNode*, FissureNormal >    fissEdgeNodes2Norm;
10399     boost::container::flat_set< const SMDS_MeshNode* > fissureNodes;
10400
10401     // choose a side of fissure
10402     fissure[0].ChooseSide();
10403     theAffectedElems.insert( fissure[0].GetMarkedElem() );
10404
10405     size_t nbCheckedBorders = 0;
10406     while ( nbCheckedBorders < fissure.size() )
10407     {
10408       // find a FissureBorder to treat
10409       FissureBorder* bord = 0;
10410       for ( size_t i = 0; i < fissure.size()  && !bord; ++i )
10411         if ( fissure[i].GetMarkedElem() )
10412           bord = & fissure[i];
10413       for ( size_t i = 0; i < fissure.size()  && !bord; ++i )
10414         if ( fissure[i].NbSub() > 0 && fissure[i]._elems[0] )
10415         {
10416           bord = & fissure[i];
10417           bord->ChooseSide();
10418           theAffectedElems.insert( bord->GetMarkedElem() );
10419         }
10420       if ( !bord ) return;
10421       ++nbCheckedBorders;
10422
10423       // treat FissureBorder's linked to bord
10424       fissureNodes.clear();
10425       fissureNodes.insert( bord->_nodes.begin(), bord->_nodes.end() );
10426       for ( size_t i = 0; i < bord->NbSub(); ++i )
10427       {
10428         TBorderLinks::iterator l2b = bord->_mappedSubs[ i ];
10429         if ( l2b == borderLinks.end() || l2b->second.empty() ) continue;
10430         std::vector< FissureBorder* >& linkedBorders = l2b->second;
10431         const SubBorder&                          sb = l2b->first;
10432         const SMDS_MeshElement*             bordElem = bord->GetMarkedElem();
10433
10434         if ( linkedBorders.size() == 1 ) // fissure edge reached, fill fissEdgeNodes2Norm
10435         {
10436           for ( int j = 0; j < sb._nbNodes; ++j )
10437             fissEdgeNodes2Norm[ sb._nodes[j] ].Add( sb._nodes[j], *bord );
10438           continue;
10439         }
10440
10441         // add to theAffectedElems elems sharing nodes of a SubBorder and a node of bordElem
10442         // until an elem adjacent to a neighbour FissureBorder is found
10443         facetNodes.clear();
10444         facetNodes.insert( facetNodes.end(), sb._nodes, sb._nodes + sb._nbNodes );
10445         facetNodes.resize( sb._nbNodes + 1 );
10446
10447         while ( bordElem )
10448         {
10449           // check if bordElem is adjacent to a neighbour FissureBorder
10450           for ( size_t j = 0; j < linkedBorders.size(); ++j )
10451           {
10452             FissureBorder* bord2 = linkedBorders[j];
10453             if ( bord2 == bord ) continue;
10454             if ( bordElem == bord2->_elems[0] || bordElem == bord2->_elems[1] )
10455               bordElem = 0;
10456             else
10457               fissureNodes.insert( bord2->_nodes.begin(), bord2->_nodes.end() );
10458           }
10459           if ( !bordElem )
10460             break;
10461
10462           // find the next bordElem
10463           const SMDS_MeshElement* nextBordElem = 0;
10464           for ( int iN = 0, nbN = bordElem->NbCornerNodes(); iN < nbN  && !nextBordElem; ++iN )
10465           {
10466             const SMDS_MeshNode* n = bordElem->GetNode( iN );
10467             if ( fissureNodes.count( n )) continue;
10468
10469             facetNodes[ sb._nbNodes ] = n;
10470             elemsByFacet.clear();
10471             if ( SMDS_Mesh::GetElementsByNodes( facetNodes, elemsByFacet ) > 1 )
10472             {
10473               for ( size_t iE = 0; iE < elemsByFacet.size(); ++iE )
10474                 if ( elemsByFacet[ iE ] != bordElem &&
10475                      !elemsByFacet[ iE ]->isMarked() )
10476                 {
10477                   theAffectedElems.insert( elemsByFacet[ iE ]);
10478                   elemsByFacet[ iE ]->setIsMarked( true );
10479                   if ( elemsByFacet[ iE ]->GetType() == bordElem->GetType() )
10480                     nextBordElem = elemsByFacet[ iE ];
10481                 }
10482             }
10483           }
10484           bordElem = nextBordElem;
10485
10486         } // while ( bordElem )
10487
10488         linkedBorders.clear(); // not to treat this link any more
10489
10490       } // loop on SubBorder's of a FissureBorder
10491
10492       bord->Clear();
10493
10494     } // loop on FissureBorder's
10495
10496
10497     // add elements sharing only one node of the fissure, except those sharing fissure edge nodes
10498
10499     // mark nodes of theAffectedElems
10500     SMESH_MeshAlgos::MarkElemNodes( theAffectedElems.begin(), theAffectedElems.end(), true );
10501
10502     // unmark nodes of the fissure
10503     elIt = theElemsOrNodes.begin();
10504     if ( (*elIt)->GetType() == SMDSAbs_Node )
10505       SMESH_MeshAlgos::MarkElems( elIt, theElemsOrNodes.end(), false );
10506     else
10507       SMESH_MeshAlgos::MarkElemNodes( elIt, theElemsOrNodes.end(), false );
10508
10509     std::vector< gp_XYZ > normVec;
10510
10511     // loop on nodes of the fissure, add elements having marked nodes
10512     for ( elIt = theElemsOrNodes.begin(); elIt != theElemsOrNodes.end(); ++elIt )
10513     {
10514       const SMDS_MeshElement* e = (*elIt);
10515       if ( e->GetType() != SMDSAbs_Node )
10516         e->setIsMarked( true ); // avoid adding a fissure element
10517
10518       for ( int iN = 0, nbN = e->NbCornerNodes(); iN < nbN; ++iN )
10519       {
10520         const SMDS_MeshNode* n = e->GetNode( iN );
10521         if ( fissEdgeNodes2Norm.count( n ))
10522           continue;
10523
10524         SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
10525         while ( invIt->more() )
10526         {
10527           const SMDS_MeshElement* eInv = invIt->next();
10528           if ( eInv->isMarked() ) continue;
10529           eInv->setIsMarked( true );
10530
10531           SMDS_ElemIteratorPtr nIt = eInv->nodesIterator();
10532           while( nIt->more() )
10533             if ( nIt->next()->isMarked())
10534             {
10535               theAffectedElems.insert( eInv );
10536               SMESH_MeshAlgos::MarkElems( eInv->nodesIterator(), true );
10537               n->setIsMarked( false );
10538               break;
10539             }
10540         }
10541       }
10542     }
10543
10544     // add elements on the fissure edge
10545     std::map< const SMDS_MeshNode*, FissureNormal >::iterator n2N;
10546     for ( n2N = fissEdgeNodes2Norm.begin(); n2N != fissEdgeNodes2Norm.end(); ++n2N )
10547     {
10548       const SMDS_MeshNode* edgeNode = n2N->first;
10549       const FissureNormal & normals = n2N->second;
10550
10551       SMDS_ElemIteratorPtr invIt = edgeNode->GetInverseElementIterator();
10552       while ( invIt->more() )
10553       {
10554         const SMDS_MeshElement* eInv = invIt->next();
10555         if ( eInv->isMarked() ) continue;
10556         eInv->setIsMarked( true );
10557
10558         // classify eInv using normals
10559         bool toAdd = normals.IsIn( edgeNode, eInv );
10560         if ( toAdd ) // check if all nodes lie on the fissure edge
10561         {
10562           bool notOnEdge = false;
10563           for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN  && !notOnEdge; ++iN )
10564             notOnEdge = !fissEdgeNodes2Norm.count( eInv->GetNode( iN ));
10565           toAdd = notOnEdge;
10566         }
10567         if ( toAdd )
10568         {
10569           theAffectedElems.insert( eInv );
10570         }
10571       }
10572     }
10573
10574     return;
10575   } // findAffectedElems()
10576 } // namespace
10577
10578 //================================================================================
10579 /*!
10580  * \brief Create elements equal (on same nodes) to given ones
10581  *  \param [in] theElements - a set of elems to duplicate. If it is empty, all
10582  *              elements of the uppest dimension are duplicated.
10583  */
10584 //================================================================================
10585
10586 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
10587 {
10588   ClearLastCreated();
10589   SMESHDS_Mesh* mesh = GetMeshDS();
10590
10591   // get an element type and an iterator over elements
10592
10593   SMDSAbs_ElementType type = SMDSAbs_All;
10594   SMDS_ElemIteratorPtr elemIt;
10595   if ( theElements.empty() )
10596   {
10597     if ( mesh->NbNodes() == 0 )
10598       return;
10599     // get most complex type
10600     SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10601       SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10602       SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10603     };
10604     for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10605       if ( mesh->GetMeshInfo().NbElements( types[i] ))
10606       {
10607         type = types[i];
10608         elemIt = mesh->elementsIterator( type );
10609         break;
10610       }
10611   }
10612   else
10613   {
10614     //type = (*theElements.begin())->GetType();
10615     elemIt = SMESHUtils::elemSetIterator( theElements );
10616   }
10617
10618   // un-mark all elements to avoid duplicating just created elements
10619   SMESH_MeshAlgos::MarkElems( mesh->elementsIterator( type ), false );
10620
10621   // duplicate elements
10622
10623   ElemFeatures elemType;
10624
10625   vector< const SMDS_MeshNode* > nodes;
10626   while ( elemIt->more() )
10627   {
10628     const SMDS_MeshElement* elem = elemIt->next();
10629     if (( type != SMDSAbs_All && elem->GetType() != type ) ||
10630         ( elem->isMarked() ))
10631       continue;
10632
10633     elemType.Init( elem, /*basicOnly=*/false );
10634     nodes.assign( elem->begin_nodes(), elem->end_nodes() );
10635
10636     if ( const SMDS_MeshElement* newElem = AddElement( nodes, elemType ))
10637       newElem->setIsMarked( true );
10638   }
10639 }
10640
10641 //================================================================================
10642 /*!
10643   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10644   \param theElems - the list of elements (edges or faces) to be replicated
10645   The nodes for duplication could be found from these elements
10646   \param theNodesNot - list of nodes to NOT replicate
10647   \param theAffectedElems - the list of elements (cells and edges) to which the
10648   replicated nodes should be associated to.
10649   \return TRUE if operation has been completed successfully, FALSE otherwise
10650 */
10651 //================================================================================
10652
10653 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10654                                     const TIDSortedElemSet& theNodesNot,
10655                                     const TIDSortedElemSet& theAffectedElems )
10656 {
10657   ClearLastCreated();
10658
10659   if ( theElems.size() == 0 )
10660     return false;
10661
10662   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10663   if ( !aMeshDS )
10664     return false;
10665
10666   bool res = false;
10667   TNodeNodeMap anOldNodeToNewNode;
10668   // duplicate elements and nodes
10669   res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10670   // replce nodes by duplications
10671   res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10672   return res;
10673 }
10674
10675 //================================================================================
10676 /*!
10677   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10678   \param theMeshDS - mesh instance
10679   \param theElems - the elements replicated or modified (nodes should be changed)
10680   \param theNodesNot - nodes to NOT replicate
10681   \param theNodeNodeMap - relation of old node to new created node
10682   \param theIsDoubleElem - flag os to replicate element or modify
10683   \return TRUE if operation has been completed successfully, FALSE otherwise
10684 */
10685 //================================================================================
10686
10687 bool SMESH_MeshEditor::doubleNodes(SMESHDS_Mesh*           theMeshDS,
10688                                    const TIDSortedElemSet& theElems,
10689                                    const TIDSortedElemSet& theNodesNot,
10690                                    TNodeNodeMap&           theNodeNodeMap,
10691                                    const bool              theIsDoubleElem )
10692 {
10693   // iterate through element and duplicate them (by nodes duplication)
10694   bool res = false;
10695   std::vector<const SMDS_MeshNode*> newNodes;
10696   ElemFeatures elemType;
10697
10698   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10699   for ( ;  elemItr != theElems.end(); ++elemItr )
10700   {
10701     const SMDS_MeshElement* anElem = *elemItr;
10702     // if (!anElem)
10703     //   continue;
10704
10705     // duplicate nodes to duplicate element
10706     bool isDuplicate = false;
10707     newNodes.resize( anElem->NbNodes() );
10708     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10709     int ind = 0;
10710     while ( anIter->more() )
10711     {
10712       const SMDS_MeshNode* aCurrNode = static_cast<const SMDS_MeshNode*>( anIter->next() );
10713       const SMDS_MeshNode*  aNewNode = aCurrNode;
10714       TNodeNodeMap::iterator     n2n = theNodeNodeMap.find( aCurrNode );
10715       if ( n2n != theNodeNodeMap.end() )
10716       {
10717         aNewNode = n2n->second;
10718       }
10719       else if ( theIsDoubleElem && !theNodesNot.count( aCurrNode ))
10720       {
10721         // duplicate node
10722         aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10723         copyPosition( aCurrNode, aNewNode );
10724         theNodeNodeMap[ aCurrNode ] = aNewNode;
10725         myLastCreatedNodes.push_back( aNewNode );
10726       }
10727       isDuplicate |= (aCurrNode != aNewNode);
10728       newNodes[ ind++ ] = aNewNode;
10729     }
10730     if ( !isDuplicate )
10731       continue;
10732
10733     if ( theIsDoubleElem )
10734       AddElement( newNodes, elemType.Init( anElem, /*basicOnly=*/false ));
10735     else
10736       theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], newNodes.size() );
10737
10738     res = true;
10739   }
10740   return res;
10741 }
10742
10743 //================================================================================
10744 /*!
10745   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10746   \param theNodes - identifiers of nodes to be doubled
10747   \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10748   nodes. If list of element identifiers is empty then nodes are doubled but
10749   they not assigned to elements
10750   \return TRUE if operation has been completed successfully, FALSE otherwise
10751 */
10752 //================================================================================
10753
10754 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10755                                     const std::list< int >& theListOfModifiedElems )
10756 {
10757   ClearLastCreated();
10758
10759   if ( theListOfNodes.size() == 0 )
10760     return false;
10761
10762   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10763   if ( !aMeshDS )
10764     return false;
10765
10766   // iterate through nodes and duplicate them
10767
10768   std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10769
10770   std::list< int >::const_iterator aNodeIter;
10771   for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10772   {
10773     const SMDS_MeshNode* aNode = aMeshDS->FindNode( *aNodeIter );
10774     if ( !aNode )
10775       continue;
10776
10777     // duplicate node
10778
10779     const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10780     if ( aNewNode )
10781     {
10782       copyPosition( aNode, aNewNode );
10783       anOldNodeToNewNode[ aNode ] = aNewNode;
10784       myLastCreatedNodes.push_back( aNewNode );
10785     }
10786   }
10787
10788   // Change nodes of elements
10789
10790   std::vector<const SMDS_MeshNode*> aNodeArr;
10791
10792   std::list< int >::const_iterator anElemIter;
10793   for ( anElemIter =  theListOfModifiedElems.begin();
10794         anElemIter != theListOfModifiedElems.end();
10795         anElemIter++ )
10796   {
10797     const SMDS_MeshElement* anElem = aMeshDS->FindElement( *anElemIter );
10798     if ( !anElem )
10799       continue;
10800
10801     aNodeArr.assign( anElem->begin_nodes(), anElem->end_nodes() );
10802     for( size_t i = 0; i < aNodeArr.size(); ++i )
10803     {
10804       std::map< const SMDS_MeshNode*, const SMDS_MeshNode* >::iterator n2n =
10805         anOldNodeToNewNode.find( aNodeArr[ i ]);
10806       if ( n2n != anOldNodeToNewNode.end() )
10807         aNodeArr[ i ] = n2n->second;
10808     }
10809     aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], aNodeArr.size() );
10810   }
10811
10812   return true;
10813 }
10814
10815 namespace {
10816
10817   //================================================================================
10818   /*!
10819     \brief Check if element located inside shape
10820     \return TRUE if IN or ON shape, FALSE otherwise
10821   */
10822   //================================================================================
10823
10824   template<class Classifier>
10825   bool isInside(const SMDS_MeshElement* theElem,
10826                 Classifier&             theClassifier,
10827                 const double            theTol)
10828   {
10829     gp_XYZ centerXYZ (0, 0, 0);
10830     for ( SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator(); aNodeItr->more(); )
10831       centerXYZ += SMESH_NodeXYZ( aNodeItr->next() );
10832
10833     gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10834     theClassifier.Perform(aPnt, theTol);
10835     TopAbs_State aState = theClassifier.State();
10836     return (aState == TopAbs_IN || aState == TopAbs_ON );
10837   }
10838
10839   //================================================================================
10840   /*!
10841    * \brief Classifier of the 3D point on the TopoDS_Face
10842    *        with interaface suitable for isInside()
10843    */
10844   //================================================================================
10845
10846   struct _FaceClassifier
10847   {
10848     Extrema_ExtPS       _extremum;
10849     BRepAdaptor_Surface _surface;
10850     TopAbs_State        _state;
10851
10852     _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10853     {
10854       _extremum.Initialize( _surface,
10855                             _surface.FirstUParameter(), _surface.LastUParameter(),
10856                             _surface.FirstVParameter(), _surface.LastVParameter(),
10857                             _surface.Tolerance(), _surface.Tolerance() );
10858     }
10859     void Perform(const gp_Pnt& aPnt, double theTol)
10860     {
10861       theTol *= theTol;
10862       _state = TopAbs_OUT;
10863       _extremum.Perform(aPnt);
10864       if ( _extremum.IsDone() )
10865         for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10866           _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10867     }
10868     TopAbs_State State() const
10869     {
10870       return _state;
10871     }
10872   };
10873 }
10874
10875 //================================================================================
10876 /*!
10877   \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
10878   This method is the first step of DoubleNodeElemGroupsInRegion.
10879   \param theElems - list of groups of elements (edges or faces) to be replicated
10880   \param theNodesNot - list of groups of nodes not to replicated
10881   \param theShape - shape to detect affected elements (element which geometric center
10882          located on or inside shape). If the shape is null, detection is done on faces orientations
10883          (select elements with a gravity center on the side given by faces normals).
10884          This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
10885          The replicated nodes should be associated to affected elements.
10886   \return true
10887   \sa DoubleNodeElemGroupsInRegion()
10888 */
10889 //================================================================================
10890
10891 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
10892                                                    const TIDSortedElemSet& theNodesNot,
10893                                                    const TopoDS_Shape&     theShape,
10894                                                    TIDSortedElemSet&       theAffectedElems)
10895 {
10896   if ( theShape.IsNull() )
10897   {
10898     findAffectedElems( theElems, theAffectedElems );
10899   }
10900   else
10901   {
10902     const double aTol = Precision::Confusion();
10903     std::unique_ptr< BRepClass3d_SolidClassifier> bsc3d;
10904     std::unique_ptr<_FaceClassifier>              aFaceClassifier;
10905     if ( theShape.ShapeType() == TopAbs_SOLID )
10906     {
10907       bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10908       bsc3d->PerformInfinitePoint(aTol);
10909     }
10910     else if (theShape.ShapeType() == TopAbs_FACE )
10911     {
10912       aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10913     }
10914
10915     // iterates on indicated elements and get elements by back references from their nodes
10916     TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10917     for ( ;  elemItr != theElems.end(); ++elemItr )
10918     {
10919       SMDS_MeshElement*     anElem = (SMDS_MeshElement*)*elemItr;
10920       SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10921       while ( nodeItr->more() )
10922       {
10923         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10924         if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10925           continue;
10926         SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10927         while ( backElemItr->more() )
10928         {
10929           const SMDS_MeshElement* curElem = backElemItr->next();
10930           if ( curElem && theElems.find(curElem) == theElems.end() &&
10931                ( bsc3d.get() ?
10932                  isInside( curElem, *bsc3d, aTol ) :
10933                  isInside( curElem, *aFaceClassifier, aTol )))
10934             theAffectedElems.insert( curElem );
10935         }
10936       }
10937     }
10938   }
10939   return true;
10940 }
10941
10942 //================================================================================
10943 /*!
10944   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10945   \param theElems - group of of elements (edges or faces) to be replicated
10946   \param theNodesNot - group of nodes not to replicate
10947   \param theShape - shape to detect affected elements (element which geometric center
10948   located on or inside shape).
10949   The replicated nodes should be associated to affected elements.
10950   \return TRUE if operation has been completed successfully, FALSE otherwise
10951 */
10952 //================================================================================
10953
10954 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
10955                                             const TIDSortedElemSet& theNodesNot,
10956                                             const TopoDS_Shape&     theShape )
10957 {
10958   if ( theShape.IsNull() )
10959     return false;
10960
10961   const double aTol = Precision::Confusion();
10962   SMESHUtils::Deleter< BRepClass3d_SolidClassifier> bsc3d;
10963   SMESHUtils::Deleter<_FaceClassifier>              aFaceClassifier;
10964   if ( theShape.ShapeType() == TopAbs_SOLID )
10965   {
10966     bsc3d._obj = new BRepClass3d_SolidClassifier( theShape );
10967     bsc3d->PerformInfinitePoint(aTol);
10968   }
10969   else if (theShape.ShapeType() == TopAbs_FACE )
10970   {
10971     aFaceClassifier._obj = new _FaceClassifier( TopoDS::Face( theShape ));
10972   }
10973
10974   // iterates on indicated elements and get elements by back references from their nodes
10975   TIDSortedElemSet anAffected;
10976   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10977   for ( ;  elemItr != theElems.end(); ++elemItr )
10978   {
10979     SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10980     if (!anElem)
10981       continue;
10982
10983     SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10984     while ( nodeItr->more() )
10985     {
10986       const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10987       if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10988         continue;
10989       SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10990       while ( backElemItr->more() )
10991       {
10992         const SMDS_MeshElement* curElem = backElemItr->next();
10993         if ( curElem && theElems.find(curElem) == theElems.end() &&
10994              ( bsc3d ?
10995                isInside( curElem, *bsc3d, aTol ) :
10996                isInside( curElem, *aFaceClassifier, aTol )))
10997           anAffected.insert( curElem );
10998       }
10999     }
11000   }
11001   return DoubleNodes( theElems, theNodesNot, anAffected );
11002 }
11003
11004 /*!
11005  *  \brief compute an oriented angle between two planes defined by four points.
11006  *  The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
11007  *  @param p0 base of the rotation axe
11008  *  @param p1 extremity of the rotation axe
11009  *  @param g1 belongs to the first plane
11010  *  @param g2 belongs to the second plane
11011  */
11012 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
11013 {
11014   gp_Vec vref(p0, p1);
11015   gp_Vec v1(p0, g1);
11016   gp_Vec v2(p0, g2);
11017   gp_Vec n1 = vref.Crossed(v1);
11018   gp_Vec n2 = vref.Crossed(v2);
11019   try {
11020     return n2.AngleWithRef(n1, vref);
11021   }
11022   catch ( Standard_Failure ) {
11023   }
11024   return Max( v1.Magnitude(), v2.Magnitude() );
11025 }
11026
11027 /*!
11028  * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11029  *  The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
11030  * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
11031  * Triangles are transformed into prisms, and quadrangles into hexahedrons.
11032  * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
11033  * 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.
11034  * 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.
11035  * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
11036  * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
11037  * \param theElems - list of groups of volumes, where a group of volume is a set of
11038  *        SMDS_MeshElements sorted by Id.
11039  * \param createJointElems - if TRUE, create the elements
11040  * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
11041  *        the boundary between \a theDomains and the rest mesh
11042  * \return TRUE if operation has been completed successfully, FALSE otherwise
11043  */
11044 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11045                                                      bool                                 createJointElems,
11046                                                      bool                                 onAllBoundaries)
11047 {
11048   // MESSAGE("----------------------------------------------");
11049   // MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11050   // MESSAGE("----------------------------------------------");
11051
11052   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11053   meshDS->BuildDownWardConnectivity(true);
11054   CHRONO(50);
11055   SMDS_UnstructuredGrid *grid = meshDS->GetGrid();
11056
11057   // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11058   //     build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11059   //     build the list of nodes shared by 2 or more domains, with their domain indexes
11060
11061   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11062   std::map<int,int>celldom; // cell vtkId --> domain
11063   std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains;  // oldNode --> (id domain --> id cell)
11064   std::map<int, std::map<int,int> > nodeDomains; // oldId -->  (domainId --> newId)
11065   faceDomains.clear();
11066   celldom.clear();
11067   cellDomains.clear();
11068   nodeDomains.clear();
11069   std::map<int,int> emptyMap;
11070   std::set<int> emptySet;
11071   emptyMap.clear();
11072
11073   //MESSAGE(".. Number of domains :"<<theElems.size());
11074
11075   TIDSortedElemSet theRestDomElems;
11076   const int iRestDom  = -1;
11077   const int idom0     = onAllBoundaries ? iRestDom : 0;
11078   const int nbDomains = theElems.size();
11079
11080   // Check if the domains do not share an element
11081   for (int idom = 0; idom < nbDomains-1; idom++)
11082   {
11083     //       MESSAGE("... Check of domain #" << idom);
11084     const TIDSortedElemSet& domain = theElems[idom];
11085     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11086     for (; elemItr != domain.end(); ++elemItr)
11087     {
11088       const SMDS_MeshElement* anElem = *elemItr;
11089       int idombisdeb = idom + 1 ;
11090       // check if the element belongs to a domain further in the list
11091       for ( size_t idombis = idombisdeb; idombis < theElems.size(); idombis++ )
11092       {
11093         const TIDSortedElemSet& domainbis = theElems[idombis];
11094         if ( domainbis.count( anElem ))
11095         {
11096           MESSAGE(".... Domain #" << idom);
11097           MESSAGE(".... Domain #" << idombis);
11098           throw SALOME_Exception("The domains are not disjoint.");
11099           return false ;
11100         }
11101       }
11102     }
11103   }
11104
11105   for (int idom = 0; idom < nbDomains; idom++)
11106   {
11107
11108     // --- build a map (face to duplicate --> volume to modify)
11109     //     with all the faces shared by 2 domains (group of elements)
11110     //     and corresponding volume of this domain, for each shared face.
11111     //     a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11112
11113     //MESSAGE("... Neighbors of domain #" << idom);
11114     const TIDSortedElemSet& domain = theElems[idom];
11115     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11116     for (; elemItr != domain.end(); ++elemItr)
11117     {
11118       const SMDS_MeshElement* anElem = *elemItr;
11119       if (!anElem)
11120         continue;
11121       int vtkId = anElem->GetVtkID();
11122       //MESSAGE("  vtkId " << vtkId << " smdsId " << anElem->GetID());
11123       int neighborsVtkIds[NBMAXNEIGHBORS];
11124       int downIds[NBMAXNEIGHBORS];
11125       unsigned char downTypes[NBMAXNEIGHBORS];
11126       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11127       for (int n = 0; n < nbNeighbors; n++)
11128       {
11129         int smdsId = meshDS->FromVtkToSmds(neighborsVtkIds[n]);
11130         const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11131         if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
11132         {
11133           bool ok = false;
11134           for ( size_t idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
11135           {
11136             // MESSAGE("Domain " << idombis);
11137             const TIDSortedElemSet& domainbis = theElems[idombis];
11138             if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11139           }
11140           if ( ok || onAllBoundaries ) // the characteristics of the face is stored
11141           {
11142             DownIdType face(downIds[n], downTypes[n]);
11143             if (!faceDomains[face].count(idom))
11144             {
11145               faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11146               celldom[vtkId] = idom;
11147               //MESSAGE("       cell with a border " << vtkId << " domain " << idom);
11148             }
11149             if ( !ok )
11150             {
11151               theRestDomElems.insert( elem );
11152               faceDomains[face][iRestDom] = neighborsVtkIds[n];
11153               celldom[neighborsVtkIds[n]] = iRestDom;
11154             }
11155           }
11156         }
11157       }
11158     }
11159   }
11160
11161   //MESSAGE("Number of shared faces " << faceDomains.size());
11162   std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11163
11164   // --- explore the shared faces domain by domain,
11165   //     explore the nodes of the face and see if they belong to a cell in the domain,
11166   //     which has only a node or an edge on the border (not a shared face)
11167
11168   for (int idomain = idom0; idomain < nbDomains; idomain++)
11169   {
11170     //MESSAGE("Domain " << idomain);
11171     const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
11172     itface = faceDomains.begin();
11173     for (; itface != faceDomains.end(); ++itface)
11174     {
11175       const std::map<int, int>& domvol = itface->second;
11176       if (!domvol.count(idomain))
11177         continue;
11178       DownIdType face = itface->first;
11179       //MESSAGE(" --- face " << face.cellId);
11180       std::set<int> oldNodes;
11181       oldNodes.clear();
11182       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11183       std::set<int>::iterator itn = oldNodes.begin();
11184       for (; itn != oldNodes.end(); ++itn)
11185       {
11186         int oldId = *itn;
11187         //MESSAGE("     node " << oldId);
11188         vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11189         for (int i=0; i<l.ncells; i++)
11190         {
11191           int vtkId = l.cells[i];
11192           const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->FromVtkToSmds(vtkId));
11193           if (!domain.count(anElem))
11194             continue;
11195           int vtkType = grid->GetCellType(vtkId);
11196           int downId = grid->CellIdToDownId(vtkId);
11197           if (downId < 0)
11198           {
11199             MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11200             continue; // not OK at this stage of the algorithm:
11201             //no cells created after BuildDownWardConnectivity
11202           }
11203           DownIdType aCell(downId, vtkType);
11204           cellDomains[aCell][idomain] = vtkId;
11205           celldom[vtkId] = idomain;
11206           //MESSAGE("       cell " << vtkId << " domain " << idomain);
11207         }
11208       }
11209     }
11210   }
11211
11212   // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11213   //     for each shared face, get the nodes
11214   //     for each node, for each domain of the face, create a clone of the node
11215
11216   // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11217   //     junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11218   //     the value is the ordered domain ids. (more than 4 domains not taken into account)
11219
11220   std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11221   std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11222   std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11223
11224   //MESSAGE(".. Duplication of the nodes");
11225   for (int idomain = idom0; idomain < nbDomains; idomain++)
11226   {
11227     itface = faceDomains.begin();
11228     for (; itface != faceDomains.end(); ++itface)
11229     {
11230       const std::map<int, int>& domvol = itface->second;
11231       if (!domvol.count(idomain))
11232         continue;
11233       DownIdType face = itface->first;
11234       //MESSAGE(" --- face " << face.cellId);
11235       std::set<int> oldNodes;
11236       oldNodes.clear();
11237       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11238       std::set<int>::iterator itn = oldNodes.begin();
11239       for (; itn != oldNodes.end(); ++itn)
11240       {
11241         int oldId = *itn;
11242         if (nodeDomains[oldId].empty())
11243         {
11244           nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11245           //MESSAGE("-+-+-b     oldNode " << oldId << " domain " << idomain);
11246         }
11247         std::map<int, int>::const_iterator itdom = domvol.begin();
11248         for (; itdom != domvol.end(); ++itdom)
11249         {
11250           int idom = itdom->first;
11251           //MESSAGE("         domain " << idom);
11252           if (!nodeDomains[oldId].count(idom)) // --- node to clone
11253           {
11254             if (nodeDomains[oldId].size() >= 2) // a multiple node
11255             {
11256               vector<int> orderedDoms;
11257               //MESSAGE("multiple node " << oldId);
11258               if (mutipleNodes.count(oldId))
11259                 orderedDoms = mutipleNodes[oldId];
11260               else
11261               {
11262                 map<int,int>::iterator it = nodeDomains[oldId].begin();
11263                 for (; it != nodeDomains[oldId].end(); ++it)
11264                   orderedDoms.push_back(it->first);
11265               }
11266               orderedDoms.push_back(idom); // TODO order ==> push_front or back
11267               //stringstream txt;
11268               //for (int i=0; i<orderedDoms.size(); i++)
11269               //  txt << orderedDoms[i] << " ";
11270               //MESSAGE("orderedDoms " << txt.str());
11271               mutipleNodes[oldId] = orderedDoms;
11272             }
11273             double *coords = grid->GetPoint(oldId);
11274             SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11275             copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11276             int newId = newNode->GetVtkID();
11277             nodeDomains[oldId][idom] = newId; // cloned node for other domains
11278             //MESSAGE("-+-+-c     oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11279           }
11280         }
11281       }
11282     }
11283   }
11284
11285   //MESSAGE(".. Creation of elements");
11286   for (int idomain = idom0; idomain < nbDomains; idomain++)
11287   {
11288     itface = faceDomains.begin();
11289     for (; itface != faceDomains.end(); ++itface)
11290     {
11291       std::map<int, int> domvol = itface->second;
11292       if (!domvol.count(idomain))
11293         continue;
11294       DownIdType face = itface->first;
11295       //MESSAGE(" --- face " << face.cellId);
11296       std::set<int> oldNodes;
11297       oldNodes.clear();
11298       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11299       int nbMultipleNodes = 0;
11300       std::set<int>::iterator itn = oldNodes.begin();
11301       for (; itn != oldNodes.end(); ++itn)
11302       {
11303         int oldId = *itn;
11304         if (mutipleNodes.count(oldId))
11305           nbMultipleNodes++;
11306       }
11307       if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11308       {
11309         //MESSAGE("multiple Nodes detected on a shared face");
11310         int downId = itface->first.cellId;
11311         unsigned char cellType = itface->first.cellType;
11312         // --- shared edge or shared face ?
11313         if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11314         {
11315           int nodes[3];
11316           int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11317           for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11318             if (mutipleNodes.count(nodes[i]))
11319               if (!mutipleNodesToFace.count(nodes[i]))
11320                 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11321         }
11322         else // shared face (between two volumes)
11323         {
11324           int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11325           const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11326           const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11327           for (int ie =0; ie < nbEdges; ie++)
11328           {
11329             int nodes[3];
11330             int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11331             if ( mutipleNodes.count(nodes[0]) && mutipleNodes.count( nodes[ nbNodes-1 ]))
11332             {
11333               vector<int> vn0 = mutipleNodes[nodes[0]];
11334               vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11335               vector<int> doms;
11336               for ( size_t i0 = 0; i0 < vn0.size(); i0++ )
11337                 for ( size_t i1 = 0; i1 < vn1.size(); i1++ )
11338                   if ( vn0[i0] == vn1[i1] )
11339                     doms.push_back( vn0[ i0 ]);
11340               if ( doms.size() > 2 )
11341               {
11342                 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11343                 double *coords = grid->GetPoint(nodes[0]);
11344                 gp_Pnt p0(coords[0], coords[1], coords[2]);
11345                 coords = grid->GetPoint(nodes[nbNodes - 1]);
11346                 gp_Pnt p1(coords[0], coords[1], coords[2]);
11347                 gp_Pnt gref;
11348                 int vtkVolIds[1000];  // an edge can belong to a lot of volumes
11349                 map<int, SMDS_MeshVolume*> domvol; // domain --> a volume with the edge
11350                 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11351                 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11352                 for ( size_t id = 0; id < doms.size(); id++ )
11353                 {
11354                   int idom = doms[id];
11355                   const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11356                   for ( int ivol = 0; ivol < nbvol; ivol++ )
11357                   {
11358                     int smdsId = meshDS->FromVtkToSmds(vtkVolIds[ivol]);
11359                     const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11360                     if (domain.count(elem))
11361                     {
11362                       const SMDS_MeshVolume* svol = SMDS_Mesh::DownCast<SMDS_MeshVolume>(elem);
11363                       domvol[idom] = (SMDS_MeshVolume*) svol;
11364                       //MESSAGE("  domain " << idom << " volume " << elem->GetID());
11365                       double values[3] = { 0,0,0 };
11366                       vtkIdType npts = 0;
11367                       vtkIdType* pts = 0;
11368                       grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11369                       for ( vtkIdType i = 0; i < npts; ++i )
11370                       {
11371                         double *coords = grid->GetPoint( pts[i] );
11372                         for ( int j = 0; j < 3; ++j )
11373                           values[j] += coords[j] / npts;
11374                       }
11375                       if ( id == 0 )
11376                       {
11377                         gref.SetCoord( values[0], values[1], values[2] );
11378                         angleDom[idom] = 0;
11379                       }
11380                       else
11381                       {
11382                         gp_Pnt g( values[0], values[1], values[2] );
11383                         angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11384                         //MESSAGE("  angle=" << angleDom[idom]);
11385                       }
11386                       break;
11387                     }
11388                   }
11389                 }
11390                 map<double, int> sortedDom; // sort domains by angle
11391                 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11392                   sortedDom[ia->second] = ia->first;
11393                 vector<int> vnodes;
11394                 vector<int> vdom;
11395                 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11396                 {
11397                   vdom.push_back(ib->second);
11398                   //MESSAGE("  ordered domain " << ib->second << "  angle " << ib->first);
11399                 }
11400                 for (int ino = 0; ino < nbNodes; ino++)
11401                   vnodes.push_back(nodes[ino]);
11402                 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11403               }
11404             }
11405           }
11406         }
11407       }
11408     }
11409   }
11410
11411   // --- iterate on shared faces (volumes to modify, face to extrude)
11412   //     get node id's of the face (id SMDS = id VTK)
11413   //     create flat element with old and new nodes if requested
11414
11415   // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11416   //     (domain1 X domain2) = domain1 + MAXINT*domain2
11417
11418   std::map<int, std::map<long,int> > nodeQuadDomains;
11419   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11420
11421   //MESSAGE(".. Creation of elements: simple junction");
11422   if (createJointElems)
11423   {
11424     string joints2DName = "joints2D";
11425     mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str());
11426     SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11427     string joints3DName = "joints3D";
11428     mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str());
11429     SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11430
11431     itface = faceDomains.begin();
11432     for (; itface != faceDomains.end(); ++itface)
11433     {
11434       DownIdType face = itface->first;
11435       std::set<int> oldNodes;
11436       std::set<int>::iterator itn;
11437       oldNodes.clear();
11438       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11439
11440       std::map<int, int> domvol = itface->second;
11441       std::map<int, int>::iterator itdom = domvol.begin();
11442       int dom1 = itdom->first;
11443       int vtkVolId = itdom->second;
11444       itdom++;
11445       int dom2 = itdom->first;
11446       SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11447                                                        nodeQuadDomains);
11448       stringstream grpname;
11449       grpname << "j_";
11450       if (dom1 < dom2)
11451         grpname << dom1 << "_" << dom2;
11452       else
11453         grpname << dom2 << "_" << dom1;
11454       string namegrp = grpname.str();
11455       if (!mapOfJunctionGroups.count(namegrp))
11456         mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str());
11457       SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11458       if (sgrp)
11459         sgrp->Add(vol->GetID());
11460       if (vol->GetType() == SMDSAbs_Volume)
11461         joints3DGrp->Add(vol->GetID());
11462       else if (vol->GetType() == SMDSAbs_Face)
11463         joints2DGrp->Add(vol->GetID());
11464     }
11465   }
11466
11467   // --- create volumes on multiple domain intersection if requested
11468   //     iterate on mutipleNodesToFace
11469   //     iterate on edgesMultiDomains
11470
11471   //MESSAGE(".. Creation of elements: multiple junction");
11472   if (createJointElems)
11473   {
11474     // --- iterate on mutipleNodesToFace
11475
11476     std::map<int, std::vector<int> >::iterator itn =  mutipleNodesToFace.begin();
11477     for (; itn != mutipleNodesToFace.end(); ++itn)
11478     {
11479       int node = itn->first;
11480       vector<int> orderDom = itn->second;
11481       vector<vtkIdType> orderedNodes;
11482       for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11483         orderedNodes.push_back( nodeDomains[ node ][ orderDom[ idom ]]);
11484       SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11485
11486       stringstream grpname;
11487       grpname << "m2j_";
11488       grpname << 0 << "_" << 0;
11489       string namegrp = grpname.str();
11490       if (!mapOfJunctionGroups.count(namegrp))
11491         mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str());
11492       SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11493       if (sgrp)
11494         sgrp->Add(face->GetID());
11495     }
11496
11497     // --- iterate on edgesMultiDomains
11498
11499     std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11500     for (; ite != edgesMultiDomains.end(); ++ite)
11501     {
11502       vector<int> nodes = ite->first;
11503       vector<int> orderDom = ite->second;
11504       vector<vtkIdType> orderedNodes;
11505       if (nodes.size() == 2)
11506       {
11507         //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11508         for ( size_t ino = 0; ino < nodes.size(); ino++ )
11509           if ( orderDom.size() == 3 )
11510             for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11511               orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11512           else
11513             for (int idom = orderDom.size()-1; idom >=0; idom--)
11514               orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11515         SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11516
11517         string namegrp = "jointsMultiples";
11518         if (!mapOfJunctionGroups.count(namegrp))
11519           mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str());
11520         SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11521         if (sgrp)
11522           sgrp->Add(vol->GetID());
11523       }
11524       else
11525       {
11526         //INFOS("Quadratic multiple joints not implemented");
11527         // TODO quadratic nodes
11528       }
11529     }
11530   }
11531
11532   // --- list the explicit faces and edges of the mesh that need to be modified,
11533   //     i.e. faces and edges built with one or more duplicated nodes.
11534   //     associate these faces or edges to their corresponding domain.
11535   //     only the first domain found is kept when a face or edge is shared
11536
11537   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11538   std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11539   faceOrEdgeDom.clear();
11540   feDom.clear();
11541
11542   //MESSAGE(".. Modification of elements");
11543   for (int idomain = idom0; idomain < nbDomains; idomain++)
11544   {
11545     std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11546     for (; itnod != nodeDomains.end(); ++itnod)
11547     {
11548       int oldId = itnod->first;
11549       //MESSAGE("     node " << oldId);
11550       vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11551       for (int i = 0; i < l.ncells; i++)
11552       {
11553         int vtkId = l.cells[i];
11554         int vtkType = grid->GetCellType(vtkId);
11555         int downId = grid->CellIdToDownId(vtkId);
11556         if (downId < 0)
11557           continue; // new cells: not to be modified
11558         DownIdType aCell(downId, vtkType);
11559         int volParents[1000];
11560         int nbvol = grid->GetParentVolumes(volParents, vtkId);
11561         for (int j = 0; j < nbvol; j++)
11562           if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11563             if (!feDom.count(vtkId))
11564             {
11565               feDom[vtkId] = idomain;
11566               faceOrEdgeDom[aCell] = emptyMap;
11567               faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11568               //MESSAGE("affect cell " << this->GetMeshDS()->FromVtkToSmds(vtkId) << " domain " << idomain
11569               //        << " type " << vtkType << " downId " << downId);
11570             }
11571       }
11572     }
11573   }
11574
11575   // --- iterate on shared faces (volumes to modify, face to extrude)
11576   //     get node id's of the face
11577   //     replace old nodes by new nodes in volumes, and update inverse connectivity
11578
11579   std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11580   for (int m=0; m<3; m++)
11581   {
11582     std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11583     itface = (*amap).begin();
11584     for (; itface != (*amap).end(); ++itface)
11585     {
11586       DownIdType face = itface->first;
11587       std::set<int> oldNodes;
11588       std::set<int>::iterator itn;
11589       oldNodes.clear();
11590       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11591       //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11592       std::map<int, int> localClonedNodeIds;
11593
11594       std::map<int, int> domvol = itface->second;
11595       std::map<int, int>::iterator itdom = domvol.begin();
11596       for (; itdom != domvol.end(); ++itdom)
11597       {
11598         int idom = itdom->first;
11599         int vtkVolId = itdom->second;
11600         //MESSAGE("modify nodes of cell " << this->GetMeshDS()->FromVtkToSmds(vtkVolId) << " domain " << idom);
11601         localClonedNodeIds.clear();
11602         for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11603         {
11604           int oldId = *itn;
11605           if (nodeDomains[oldId].count(idom))
11606           {
11607             localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11608             //MESSAGE("     node " << oldId << " --> " << localClonedNodeIds[oldId]);
11609           }
11610         }
11611         meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11612       }
11613     }
11614   }
11615
11616   // Remove empty groups (issue 0022812)
11617   std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
11618   for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
11619   {
11620     if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
11621       myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
11622   }
11623
11624   meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11625   grid->DeleteLinks();
11626
11627   CHRONOSTOP(50);
11628   counters::stats();
11629   return true;
11630 }
11631
11632 /*!
11633  * \brief Double nodes on some external faces and create flat elements.
11634  * Flat elements are mainly used by some types of mechanic calculations.
11635  *
11636  * Each group of the list must be constituted of faces.
11637  * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11638  * @param theElems - list of groups of faces, where a group of faces is a set of
11639  * SMDS_MeshElements sorted by Id.
11640  * @return TRUE if operation has been completed successfully, FALSE otherwise
11641  */
11642 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11643 {
11644   // MESSAGE("-------------------------------------------------");
11645   // MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11646   // MESSAGE("-------------------------------------------------");
11647
11648   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11649
11650   // --- For each group of faces
11651   //     duplicate the nodes, create a flat element based on the face
11652   //     replace the nodes of the faces by their clones
11653
11654   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11655   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11656   clonedNodes.clear();
11657   intermediateNodes.clear();
11658   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11659   mapOfJunctionGroups.clear();
11660
11661   for ( size_t idom = 0; idom < theElems.size(); idom++ )
11662   {
11663     const TIDSortedElemSet&           domain = theElems[idom];
11664     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11665     for ( ; elemItr != domain.end(); ++elemItr )
11666     {
11667       const SMDS_MeshFace* aFace = meshDS->DownCast<SMDS_MeshFace> ( *elemItr );
11668       if (!aFace)
11669         continue;
11670       // MESSAGE("aFace=" << aFace->GetID());
11671       bool isQuad = aFace->IsQuadratic();
11672       vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11673
11674       // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11675
11676       SMDS_NodeIteratorPtr nodeIt = aFace->nodeIterator();
11677       while (nodeIt->more())
11678       {
11679         const SMDS_MeshNode* node = nodeIt->next();
11680         bool isMedium = ( isQuad && aFace->IsMediumNode( node ));
11681         if (isMedium)
11682           ln2.push_back(node);
11683         else
11684           ln0.push_back(node);
11685
11686         const SMDS_MeshNode* clone = 0;
11687         if (!clonedNodes.count(node))
11688         {
11689           clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11690           copyPosition( node, clone );
11691           clonedNodes[node] = clone;
11692         }
11693         else
11694           clone = clonedNodes[node];
11695
11696         if (isMedium)
11697           ln3.push_back(clone);
11698         else
11699           ln1.push_back(clone);
11700
11701         const SMDS_MeshNode* inter = 0;
11702         if (isQuad && (!isMedium))
11703         {
11704           if (!intermediateNodes.count(node))
11705           {
11706             inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11707             copyPosition( node, inter );
11708             intermediateNodes[node] = inter;
11709           }
11710           else
11711             inter = intermediateNodes[node];
11712           ln4.push_back(inter);
11713         }
11714       }
11715
11716       // --- extrude the face
11717
11718       vector<const SMDS_MeshNode*> ln;
11719       SMDS_MeshVolume* vol = 0;
11720       vtkIdType aType = aFace->GetVtkType();
11721       switch (aType)
11722       {
11723       case VTK_TRIANGLE:
11724         vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11725         // MESSAGE("vol prism " << vol->GetID());
11726         ln.push_back(ln1[0]);
11727         ln.push_back(ln1[1]);
11728         ln.push_back(ln1[2]);
11729         break;
11730       case VTK_QUAD:
11731         vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11732         // MESSAGE("vol hexa " << vol->GetID());
11733         ln.push_back(ln1[0]);
11734         ln.push_back(ln1[1]);
11735         ln.push_back(ln1[2]);
11736         ln.push_back(ln1[3]);
11737         break;
11738       case VTK_QUADRATIC_TRIANGLE:
11739         vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11740                                 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11741         // MESSAGE("vol quad prism " << vol->GetID());
11742         ln.push_back(ln1[0]);
11743         ln.push_back(ln1[1]);
11744         ln.push_back(ln1[2]);
11745         ln.push_back(ln3[0]);
11746         ln.push_back(ln3[1]);
11747         ln.push_back(ln3[2]);
11748         break;
11749       case VTK_QUADRATIC_QUAD:
11750         //              vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11751         //                                      ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11752         //                                      ln4[0], ln4[1], ln4[2], ln4[3]);
11753         vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11754                                 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11755                                 ln4[0], ln4[1], ln4[2], ln4[3]);
11756         // MESSAGE("vol quad hexa " << vol->GetID());
11757         ln.push_back(ln1[0]);
11758         ln.push_back(ln1[1]);
11759         ln.push_back(ln1[2]);
11760         ln.push_back(ln1[3]);
11761         ln.push_back(ln3[0]);
11762         ln.push_back(ln3[1]);
11763         ln.push_back(ln3[2]);
11764         ln.push_back(ln3[3]);
11765         break;
11766       case VTK_POLYGON:
11767         break;
11768       default:
11769         break;
11770       }
11771
11772       if (vol)
11773       {
11774         stringstream grpname;
11775         grpname << "jf_";
11776         grpname << idom;
11777         string namegrp = grpname.str();
11778         if (!mapOfJunctionGroups.count(namegrp))
11779           mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str());
11780         SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11781         if (sgrp)
11782           sgrp->Add(vol->GetID());
11783       }
11784
11785       // --- modify the face
11786
11787       const_cast<SMDS_MeshFace*>( aFace )->ChangeNodes( &ln[0], ln.size() );
11788     }
11789   }
11790   return true;
11791 }
11792
11793 /*!
11794  *  \brief identify all the elements around a geom shape, get the faces delimiting the hole
11795  *  Build groups of volume to remove, groups of faces to replace on the skin of the object,
11796  *  groups of faces to remove inside the object, (idem edges).
11797  *  Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11798  */
11799 void SMESH_MeshEditor::CreateHoleSkin(double                          radius,
11800                                       const TopoDS_Shape&             theShape,
11801                                       SMESH_NodeSearcher*             theNodeSearcher,
11802                                       const char*                     groupName,
11803                                       std::vector<double>&            nodesCoords,
11804                                       std::vector<std::vector<int> >& listOfListOfNodes)
11805 {
11806   // MESSAGE("--------------------------------");
11807   // MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11808   // MESSAGE("--------------------------------");
11809
11810   // --- zone of volumes to remove is given :
11811   //     1 either by a geom shape (one or more vertices) and a radius,
11812   //     2 either by a group of nodes (representative of the shape)to use with the radius,
11813   //     3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11814   //     In the case 2, the group of nodes is an external group of nodes from another mesh,
11815   //     In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11816   //     defined by it's name.
11817
11818   SMESHDS_GroupBase* groupDS = 0;
11819   SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11820   while ( groupIt->more() )
11821   {
11822     groupDS = 0;
11823     SMESH_Group * group = groupIt->next();
11824     if ( !group ) continue;
11825     groupDS = group->GetGroupDS();
11826     if ( !groupDS || groupDS->IsEmpty() ) continue;
11827     std::string grpName = group->GetName();
11828     //MESSAGE("grpName=" << grpName);
11829     if (grpName == groupName)
11830       break;
11831     else
11832       groupDS = 0;
11833   }
11834
11835   bool isNodeGroup = false;
11836   bool isNodeCoords = false;
11837   if (groupDS)
11838   {
11839     if (groupDS->GetType() != SMDSAbs_Node)
11840       return;
11841     isNodeGroup = true;     // a group of nodes exists and it is in this mesh
11842   }
11843
11844   if (nodesCoords.size() > 0)
11845     isNodeCoords = true; // a list o nodes given by their coordinates
11846   //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11847
11848   // --- define groups to build
11849
11850   // --- group of SMDS volumes
11851   string grpvName = groupName;
11852   grpvName += "_vol";
11853   SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str());
11854   if (!grp)
11855   {
11856     MESSAGE("group not created " << grpvName);
11857     return;
11858   }
11859   SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
11860
11861   // --- group of SMDS faces on the skin
11862   string grpsName = groupName;
11863   grpsName += "_skin";
11864   SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str());
11865   if (!grps)
11866   {
11867     MESSAGE("group not created " << grpsName);
11868     return;
11869   }
11870   SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
11871
11872   // --- group of SMDS faces internal (several shapes)
11873   string grpiName = groupName;
11874   grpiName += "_internalFaces";
11875   SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str());
11876   if (!grpi)
11877   {
11878     MESSAGE("group not created " << grpiName);
11879     return;
11880   }
11881   SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
11882
11883   // --- group of SMDS faces internal (several shapes)
11884   string grpeiName = groupName;
11885   grpeiName += "_internalEdges";
11886   SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str());
11887   if (!grpei)
11888   {
11889     MESSAGE("group not created " << grpeiName);
11890     return;
11891   }
11892   SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
11893
11894   // --- build downward connectivity
11895
11896   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11897   meshDS->BuildDownWardConnectivity(true);
11898   SMDS_UnstructuredGrid* grid = meshDS->GetGrid();
11899
11900   // --- set of volumes detected inside
11901
11902   std::set<int> setOfInsideVol;
11903   std::set<int> setOfVolToCheck;
11904
11905   std::vector<gp_Pnt> gpnts;
11906   gpnts.clear();
11907
11908   if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
11909   {
11910     //MESSAGE("group of nodes provided");
11911     SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
11912     while ( elemIt->more() )
11913     {
11914       const SMDS_MeshElement* elem = elemIt->next();
11915       if (!elem)
11916         continue;
11917       const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
11918       if (!node)
11919         continue;
11920       SMDS_MeshElement* vol = 0;
11921       SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
11922       while (volItr->more())
11923       {
11924         vol = (SMDS_MeshElement*)volItr->next();
11925         setOfInsideVol.insert(vol->GetVtkID());
11926         sgrp->Add(vol->GetID());
11927       }
11928     }
11929   }
11930   else if (isNodeCoords)
11931   {
11932     //MESSAGE("list of nodes coordinates provided");
11933     size_t i = 0;
11934     int k = 0;
11935     while ( i < nodesCoords.size()-2 )
11936     {
11937       double x = nodesCoords[i++];
11938       double y = nodesCoords[i++];
11939       double z = nodesCoords[i++];
11940       gp_Pnt p = gp_Pnt(x, y ,z);
11941       gpnts.push_back(p);
11942       //MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
11943       k++;
11944     }
11945   }
11946   else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
11947   {
11948     //MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
11949     TopTools_IndexedMapOfShape vertexMap;
11950     TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
11951     gp_Pnt p = gp_Pnt(0,0,0);
11952     if (vertexMap.Extent() < 1)
11953       return;
11954
11955     for ( int i = 1; i <= vertexMap.Extent(); ++i )
11956     {
11957       const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
11958       p = BRep_Tool::Pnt(vertex);
11959       gpnts.push_back(p);
11960       //MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
11961     }
11962   }
11963
11964   if (gpnts.size() > 0)
11965   {
11966     const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
11967     //MESSAGE("startNode->nodeId " << nodeId);
11968
11969     double radius2 = radius*radius;
11970     //MESSAGE("radius2 " << radius2);
11971
11972     // --- volumes on start node
11973
11974     setOfVolToCheck.clear();
11975     SMDS_MeshElement* startVol = 0;
11976     SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
11977     while (volItr->more())
11978     {
11979       startVol = (SMDS_MeshElement*)volItr->next();
11980       setOfVolToCheck.insert(startVol->GetVtkID());
11981     }
11982     if (setOfVolToCheck.empty())
11983     {
11984       MESSAGE("No volumes found");
11985       return;
11986     }
11987
11988     // --- starting with central volumes then their neighbors, check if they are inside
11989     //     or outside the domain, until no more new neighbor volume is inside.
11990     //     Fill the group of inside volumes
11991
11992     std::map<int, double> mapOfNodeDistance2;
11993     mapOfNodeDistance2.clear();
11994     std::set<int> setOfOutsideVol;
11995     while (!setOfVolToCheck.empty())
11996     {
11997       std::set<int>::iterator it = setOfVolToCheck.begin();
11998       int vtkId = *it;
11999       //MESSAGE("volume to check,  vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12000       bool volInside = false;
12001       vtkIdType npts = 0;
12002       vtkIdType* pts = 0;
12003       grid->GetCellPoints(vtkId, npts, pts);
12004       for (int i=0; i<npts; i++)
12005       {
12006         double distance2 = 0;
12007         if (mapOfNodeDistance2.count(pts[i]))
12008         {
12009           distance2 = mapOfNodeDistance2[pts[i]];
12010           //MESSAGE("point " << pts[i] << " distance2 " << distance2);
12011         }
12012         else
12013         {
12014           double *coords = grid->GetPoint(pts[i]);
12015           gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
12016           distance2 = 1.E40;
12017           for ( size_t j = 0; j < gpnts.size(); j++ )
12018           {
12019             double d2 = aPoint.SquareDistance( gpnts[ j ]);
12020             if (d2 < distance2)
12021             {
12022               distance2 = d2;
12023               if (distance2 < radius2)
12024                 break;
12025             }
12026           }
12027           mapOfNodeDistance2[pts[i]] = distance2;
12028           //MESSAGE("  point "  << pts[i]  << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " <<  coords[2]);
12029         }
12030         if (distance2 < radius2)
12031         {
12032           volInside = true; // one or more nodes inside the domain
12033           sgrp->Add(meshDS->FromVtkToSmds(vtkId));
12034           break;
12035         }
12036       }
12037       if (volInside)
12038       {
12039         setOfInsideVol.insert(vtkId);
12040         //MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12041         int neighborsVtkIds[NBMAXNEIGHBORS];
12042         int downIds[NBMAXNEIGHBORS];
12043         unsigned char downTypes[NBMAXNEIGHBORS];
12044         int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12045         for (int n = 0; n < nbNeighbors; n++)
12046           if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12047             setOfVolToCheck.insert(neighborsVtkIds[n]);
12048       }
12049       else
12050       {
12051         setOfOutsideVol.insert(vtkId);
12052         //MESSAGE("  volume outside, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12053       }
12054       setOfVolToCheck.erase(vtkId);
12055     }
12056   }
12057
12058   // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12059   //     If yes, add the volume to the inside set
12060
12061   bool addedInside = true;
12062   std::set<int> setOfVolToReCheck;
12063   while (addedInside)
12064   {
12065     //MESSAGE(" --------------------------- re check");
12066     addedInside = false;
12067     std::set<int>::iterator itv = setOfInsideVol.begin();
12068     for (; itv != setOfInsideVol.end(); ++itv)
12069     {
12070       int vtkId = *itv;
12071       int neighborsVtkIds[NBMAXNEIGHBORS];
12072       int downIds[NBMAXNEIGHBORS];
12073       unsigned char downTypes[NBMAXNEIGHBORS];
12074       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12075       for (int n = 0; n < nbNeighbors; n++)
12076         if (!setOfInsideVol.count(neighborsVtkIds[n]))
12077           setOfVolToReCheck.insert(neighborsVtkIds[n]);
12078     }
12079     setOfVolToCheck = setOfVolToReCheck;
12080     setOfVolToReCheck.clear();
12081     while  (!setOfVolToCheck.empty())
12082     {
12083       std::set<int>::iterator it = setOfVolToCheck.begin();
12084       int vtkId = *it;
12085       if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12086       {
12087         //MESSAGE("volume to recheck,  vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12088         int countInside = 0;
12089         int neighborsVtkIds[NBMAXNEIGHBORS];
12090         int downIds[NBMAXNEIGHBORS];
12091         unsigned char downTypes[NBMAXNEIGHBORS];
12092         int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12093         for (int n = 0; n < nbNeighbors; n++)
12094           if (setOfInsideVol.count(neighborsVtkIds[n]))
12095             countInside++;
12096         //MESSAGE("countInside " << countInside);
12097         if (countInside > 1)
12098         {
12099           //MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12100           setOfInsideVol.insert(vtkId);
12101           sgrp->Add(meshDS->FromVtkToSmds(vtkId));
12102           addedInside = true;
12103         }
12104         else
12105           setOfVolToReCheck.insert(vtkId);
12106       }
12107       setOfVolToCheck.erase(vtkId);
12108     }
12109   }
12110
12111   // --- map of Downward faces at the boundary, inside the global volume
12112   //     map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12113   //     fill group of SMDS faces inside the volume (when several volume shapes)
12114   //     fill group of SMDS faces on the skin of the global volume (if skin)
12115
12116   std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12117   std::map<DownIdType, int, DownIdCompare> skinFaces;     // faces on the skin of the global volume --> corresponding cell
12118   std::set<int>::iterator it = setOfInsideVol.begin();
12119   for (; it != setOfInsideVol.end(); ++it)
12120   {
12121     int vtkId = *it;
12122     //MESSAGE("  vtkId " << vtkId  << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12123     int neighborsVtkIds[NBMAXNEIGHBORS];
12124     int downIds[NBMAXNEIGHBORS];
12125     unsigned char downTypes[NBMAXNEIGHBORS];
12126     int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12127     for (int n = 0; n < nbNeighbors; n++)
12128     {
12129       int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12130       if (neighborDim == 3)
12131       {
12132         if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12133         {
12134           DownIdType face(downIds[n], downTypes[n]);
12135           boundaryFaces[face] = vtkId;
12136         }
12137         // if the face between to volumes is in the mesh, get it (internal face between shapes)
12138         int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12139         if (vtkFaceId >= 0)
12140         {
12141           sgrpi->Add(meshDS->FromVtkToSmds(vtkFaceId));
12142           // find also the smds edges on this face
12143           int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12144           const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12145           const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12146           for (int i = 0; i < nbEdges; i++)
12147           {
12148             int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12149             if (vtkEdgeId >= 0)
12150               sgrpei->Add(meshDS->FromVtkToSmds(vtkEdgeId));
12151           }
12152         }
12153       }
12154       else if (neighborDim == 2) // skin of the volume
12155       {
12156         DownIdType face(downIds[n], downTypes[n]);
12157         skinFaces[face] = vtkId;
12158         int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12159         if (vtkFaceId >= 0)
12160           sgrps->Add(meshDS->FromVtkToSmds(vtkFaceId));
12161       }
12162     }
12163   }
12164
12165   // --- identify the edges constituting the wire of each subshape on the skin
12166   //     define polylines with the nodes of edges, equivalent to wires
12167   //     project polylines on subshapes, and partition, to get geom faces
12168
12169   std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12170   std::set<int> emptySet;
12171   emptySet.clear();
12172   std::set<int> shapeIds;
12173
12174   SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12175   while (itelem->more())
12176   {
12177     const SMDS_MeshElement *elem = itelem->next();
12178     int shapeId = elem->getshapeId();
12179     int   vtkId = elem->GetVtkID();
12180     if (!shapeIdToVtkIdSet.count(shapeId))
12181     {
12182       shapeIdToVtkIdSet[shapeId] = emptySet;
12183       shapeIds.insert(shapeId);
12184     }
12185     shapeIdToVtkIdSet[shapeId].insert(vtkId);
12186   }
12187
12188   std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12189   std::set<DownIdType, DownIdCompare> emptyEdges;
12190   emptyEdges.clear();
12191
12192   std::map<int, std::set<int> >::iterator itShape =  shapeIdToVtkIdSet.begin();
12193   for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12194   {
12195     int shapeId = itShape->first;
12196     //MESSAGE(" --- Shape ID --- "<< shapeId);
12197     shapeIdToEdges[shapeId] = emptyEdges;
12198
12199     std::vector<int> nodesEdges;
12200
12201     std::set<int>::iterator its = itShape->second.begin();
12202     for (; its != itShape->second.end(); ++its)
12203     {
12204       int vtkId = *its;
12205       //MESSAGE("     " << vtkId);
12206       int neighborsVtkIds[NBMAXNEIGHBORS];
12207       int downIds[NBMAXNEIGHBORS];
12208       unsigned char downTypes[NBMAXNEIGHBORS];
12209       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12210       for (int n = 0; n < nbNeighbors; n++)
12211       {
12212         if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12213           continue;
12214         int smdsId = meshDS->FromVtkToSmds(neighborsVtkIds[n]);
12215         const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12216         if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12217         {
12218           DownIdType edge(downIds[n], downTypes[n]);
12219           if (!shapeIdToEdges[shapeId].count(edge))
12220           {
12221             shapeIdToEdges[shapeId].insert(edge);
12222             int vtkNodeId[3];
12223             int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12224             nodesEdges.push_back(vtkNodeId[0]);
12225             nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12226             //MESSAGE("       --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12227           }
12228         }
12229       }
12230     }
12231
12232     std::list<int> order;
12233     order.clear();
12234     if (nodesEdges.size() > 0)
12235     {
12236       order.push_back(nodesEdges[0]); //MESSAGE("       --- back " << order.back()+1); // SMDS id = VTK id + 1;
12237       nodesEdges[0] = -1;
12238       order.push_back(nodesEdges[1]); //MESSAGE("       --- back " << order.back()+1);
12239       nodesEdges[1] = -1; // do not reuse this edge
12240       bool found = true;
12241       while (found)
12242       {
12243         int nodeTofind = order.back(); // try first to push back
12244         int i = 0;
12245         for ( i = 0; i < (int)nodesEdges.size(); i++ )
12246           if (nodesEdges[i] == nodeTofind)
12247             break;
12248         if ( i == (int) nodesEdges.size() )
12249           found = false; // no follower found on back
12250         else
12251         {
12252           if (i%2) // odd ==> use the previous one
12253             if (nodesEdges[i-1] < 0)
12254               found = false;
12255             else
12256             {
12257               order.push_back(nodesEdges[i-1]); //MESSAGE("       --- back " << order.back()+1);
12258               nodesEdges[i-1] = -1;
12259             }
12260           else // even ==> use the next one
12261             if (nodesEdges[i+1] < 0)
12262               found = false;
12263             else
12264             {
12265               order.push_back(nodesEdges[i+1]); //MESSAGE("       --- back " << order.back()+1);
12266               nodesEdges[i+1] = -1;
12267             }
12268         }
12269         if (found)
12270           continue;
12271         // try to push front
12272         found = true;
12273         nodeTofind = order.front(); // try to push front
12274         for ( i = 0;  i < (int)nodesEdges.size(); i++ )
12275           if ( nodesEdges[i] == nodeTofind )
12276             break;
12277         if ( i == (int)nodesEdges.size() )
12278         {
12279           found = false; // no predecessor found on front
12280           continue;
12281         }
12282         if (i%2) // odd ==> use the previous one
12283           if (nodesEdges[i-1] < 0)
12284             found = false;
12285           else
12286           {
12287             order.push_front(nodesEdges[i-1]); //MESSAGE("       --- front " << order.front()+1);
12288             nodesEdges[i-1] = -1;
12289           }
12290         else // even ==> use the next one
12291           if (nodesEdges[i+1] < 0)
12292             found = false;
12293           else
12294           {
12295             order.push_front(nodesEdges[i+1]); //MESSAGE("       --- front " << order.front()+1);
12296             nodesEdges[i+1] = -1;
12297           }
12298       }
12299     }
12300
12301
12302     std::vector<int> nodes;
12303     nodes.push_back(shapeId);
12304     std::list<int>::iterator itl = order.begin();
12305     for (; itl != order.end(); itl++)
12306     {
12307       nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12308       //MESSAGE("              ordered node " << nodes[nodes.size()-1]);
12309     }
12310     listOfListOfNodes.push_back(nodes);
12311   }
12312
12313   //     partition geom faces with blocFissure
12314   //     mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12315   //     mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12316
12317   return;
12318 }
12319
12320
12321 //================================================================================
12322 /*!
12323  * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12324  * The created 2D mesh elements based on nodes of free faces of boundary volumes
12325  * \return TRUE if operation has been completed successfully, FALSE otherwise
12326  */
12327 //================================================================================
12328
12329 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12330 {
12331   // iterates on volume elements and detect all free faces on them
12332   SMESHDS_Mesh* aMesh = GetMeshDS();
12333   if (!aMesh)
12334     return false;
12335
12336   ElemFeatures faceType( SMDSAbs_Face );
12337   int nbFree = 0, nbExisted = 0, nbCreated = 0;
12338   SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12339   while(vIt->more())
12340   {
12341     const SMDS_MeshVolume* volume = vIt->next();
12342     SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12343     vTool.SetExternalNormal();
12344     const int iQuad = volume->IsQuadratic();
12345     faceType.SetQuad( iQuad );
12346     for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12347     {
12348       if (!vTool.IsFreeFace(iface))
12349         continue;
12350       nbFree++;
12351       vector<const SMDS_MeshNode *> nodes;
12352       int nbFaceNodes = vTool.NbFaceNodes(iface);
12353       const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12354       int inode = 0;
12355       for ( ; inode < nbFaceNodes; inode += iQuad+1)
12356         nodes.push_back(faceNodes[inode]);
12357
12358       if (iQuad) // add medium nodes
12359       {
12360         for ( inode = 1; inode < nbFaceNodes; inode += 2)
12361           nodes.push_back(faceNodes[inode]);
12362         if ( nbFaceNodes == 9 ) // bi-quadratic quad
12363           nodes.push_back(faceNodes[8]);
12364       }
12365       // add new face based on volume nodes
12366       if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) )
12367       {
12368         nbExisted++; // face already exists
12369       }
12370       else
12371       {
12372         AddElement( nodes, faceType.SetPoly( nbFaceNodes/(iQuad+1) > 4 ));
12373         nbCreated++;
12374       }
12375     }
12376   }
12377   return ( nbFree == ( nbExisted + nbCreated ));
12378 }
12379
12380 namespace
12381 {
12382   inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12383   {
12384     if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12385       return n;
12386     return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12387   }
12388 }
12389 //================================================================================
12390 /*!
12391  * \brief Creates missing boundary elements
12392  *  \param elements - elements whose boundary is to be checked
12393  *  \param dimension - defines type of boundary elements to create
12394  *  \param group - a group to store created boundary elements in
12395  *  \param targetMesh - a mesh to store created boundary elements in
12396  *  \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12397  *  \param toCopyExistingBoundary - if true, not only new but also pre-existing
12398  *                                boundary elements will be copied into the targetMesh
12399  *  \param toAddExistingBondary - if true, not only new but also pre-existing
12400  *                                boundary elements will be added into the new group
12401  *  \param aroundElements - if true, elements will be created on boundary of given
12402  *                          elements else, on boundary of the whole mesh.
12403  * \return nb of added boundary elements
12404  */
12405 //================================================================================
12406
12407 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12408                                        Bnd_Dimension           dimension,
12409                                        SMESH_Group*            group/*=0*/,
12410                                        SMESH_Mesh*             targetMesh/*=0*/,
12411                                        bool                    toCopyElements/*=false*/,
12412                                        bool                    toCopyExistingBoundary/*=false*/,
12413                                        bool                    toAddExistingBondary/*= false*/,
12414                                        bool                    aroundElements/*= false*/)
12415 {
12416   SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12417   SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12418   // hope that all elements are of the same type, do not check them all
12419   if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12420     throw SALOME_Exception(LOCALIZED("wrong element type"));
12421
12422   if ( !targetMesh )
12423     toCopyElements = toCopyExistingBoundary = false;
12424
12425   SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12426   SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12427   int nbAddedBnd = 0;
12428
12429   // editor adding present bnd elements and optionally holding elements to add to the group
12430   SMESH_MeshEditor* presentEditor;
12431   SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12432   presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12433
12434   SMESH_MesherHelper helper( *myMesh );
12435   const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12436   SMDS_VolumeTool vTool;
12437   TIDSortedElemSet avoidSet;
12438   const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12439   size_t inode;
12440
12441   typedef vector<const SMDS_MeshNode*> TConnectivity;
12442   TConnectivity tgtNodes;
12443   ElemFeatures elemKind( missType ), elemToCopy;
12444
12445   vector<const SMDS_MeshElement*> presentBndElems;
12446   vector<TConnectivity>           missingBndElems;
12447   vector<int>                     freeFacets;
12448   TConnectivity nodes, elemNodes;
12449
12450   SMDS_ElemIteratorPtr eIt;
12451   if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12452   else                  eIt = SMESHUtils::elemSetIterator( elements );
12453
12454   while ( eIt->more() )
12455   {
12456     const SMDS_MeshElement* elem = eIt->next();
12457     const int              iQuad = elem->IsQuadratic();
12458     elemKind.SetQuad( iQuad );
12459
12460     // ------------------------------------------------------------------------------------
12461     // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12462     // ------------------------------------------------------------------------------------
12463     presentBndElems.clear();
12464     missingBndElems.clear();
12465     freeFacets.clear(); nodes.clear(); elemNodes.clear();
12466     if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12467     {
12468       const SMDS_MeshElement* otherVol = 0;
12469       for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12470       {
12471         if ( !vTool.IsFreeFace(iface, &otherVol) &&
12472              ( !aroundElements || elements.count( otherVol )))
12473           continue;
12474         freeFacets.push_back( iface );
12475       }
12476       if ( missType == SMDSAbs_Face )
12477         vTool.SetExternalNormal();
12478       for ( size_t i = 0; i < freeFacets.size(); ++i )
12479       {
12480         int                iface = freeFacets[i];
12481         const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12482         const size_t nbFaceNodes = vTool.NbFaceNodes (iface);
12483         if ( missType == SMDSAbs_Edge ) // boundary edges
12484         {
12485           nodes.resize( 2+iQuad );
12486           for ( size_t i = 0; i < nbFaceNodes; i += 1+iQuad )
12487           {
12488             for ( size_t j = 0; j < nodes.size(); ++j )
12489               nodes[ j ] = nn[ i+j ];
12490             if ( const SMDS_MeshElement* edge =
12491                  aMesh->FindElement( nodes, SMDSAbs_Edge, /*noMedium=*/false ))
12492               presentBndElems.push_back( edge );
12493             else
12494               missingBndElems.push_back( nodes );
12495           }
12496         }
12497         else // boundary face
12498         {
12499           nodes.clear();
12500           for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12501             nodes.push_back( nn[inode] ); // add corner nodes
12502           if (iQuad)
12503             for ( inode = 1; inode < nbFaceNodes; inode += 2)
12504               nodes.push_back( nn[inode] ); // add medium nodes
12505           int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12506           if ( iCenter > 0 )
12507             nodes.push_back( vTool.GetNodes()[ iCenter ] );
12508
12509           if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12510                                                                SMDSAbs_Face, /*noMedium=*/false ))
12511             presentBndElems.push_back( f );
12512           else
12513             missingBndElems.push_back( nodes );
12514
12515           if ( targetMesh != myMesh )
12516           {
12517             // add 1D elements on face boundary to be added to a new mesh
12518             const SMDS_MeshElement* edge;
12519             for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12520             {
12521               if ( iQuad )
12522                 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12523               else
12524                 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12525               if ( edge && avoidSet.insert( edge ).second )
12526                 presentBndElems.push_back( edge );
12527             }
12528           }
12529         }
12530       }
12531     }
12532     else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12533     {
12534       avoidSet.clear(), avoidSet.insert( elem );
12535       elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesIterator() ),
12536                         SMDS_MeshElement::iterator() );
12537       elemNodes.push_back( elemNodes[0] );
12538       nodes.resize( 2 + iQuad );
12539       const int nbLinks = elem->NbCornerNodes();
12540       for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12541       {
12542         nodes[0] = elemNodes[iN];
12543         nodes[1] = elemNodes[iN+1+iQuad];
12544         if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12545           continue; // not free link
12546
12547         if ( iQuad ) nodes[2] = elemNodes[iN+1];
12548         if ( const SMDS_MeshElement* edge =
12549              aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12550           presentBndElems.push_back( edge );
12551         else
12552           missingBndElems.push_back( nodes );
12553       }
12554     }
12555
12556     // ---------------------------------
12557     // 2. Add missing boundary elements
12558     // ---------------------------------
12559     if ( targetMesh != myMesh )
12560       // instead of making a map of nodes in this mesh and targetMesh,
12561       // we create nodes with same IDs.
12562       for ( size_t i = 0; i < missingBndElems.size(); ++i )
12563       {
12564         TConnectivity& srcNodes = missingBndElems[i];
12565         tgtNodes.resize( srcNodes.size() );
12566         for ( inode = 0; inode < srcNodes.size(); ++inode )
12567           tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12568         if ( /*aroundElements && */tgtEditor.GetMeshDS()->FindElement( tgtNodes,
12569                                                                        missType,
12570                                                                        /*noMedium=*/false))
12571           continue;
12572         tgtEditor.AddElement( tgtNodes, elemKind.SetPoly( tgtNodes.size()/(iQuad+1) > 4 ));
12573         ++nbAddedBnd;
12574       }
12575     else
12576       for ( size_t i = 0; i < missingBndElems.size(); ++i )
12577       {
12578         TConnectivity& nodes = missingBndElems[ i ];
12579         if ( /*aroundElements && */tgtEditor.GetMeshDS()->FindElement( nodes,
12580                                                                        missType,
12581                                                                        /*noMedium=*/false))
12582           continue;
12583         SMDS_MeshElement* newElem =
12584           tgtEditor.AddElement( nodes, elemKind.SetPoly( nodes.size()/(iQuad+1) > 4 ));
12585         nbAddedBnd += bool( newElem );
12586
12587         // try to set a new element to a shape
12588         if ( myMesh->HasShapeToMesh() )
12589         {
12590           bool ok = true;
12591           set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12592           const size_t nbN = nodes.size() / (iQuad+1 );
12593           for ( inode = 0; inode < nbN && ok; ++inode )
12594           {
12595             pair<int, TopAbs_ShapeEnum> i_stype =
12596               helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12597             if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12598               mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12599           }
12600           if ( ok && mediumShapes.size() > 1 )
12601           {
12602             set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12603             pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12604             for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12605             {
12606               if (( ok = ( stype_i->first != stype_i_0.first )))
12607                 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12608                                         aMesh->IndexToShape( stype_i_0.second ));
12609             }
12610           }
12611           if ( ok && mediumShapes.begin()->first == missShapeType )
12612             aMesh->SetMeshElementOnShape( newElem, mediumShapes.begin()->second );
12613         }
12614       }
12615
12616     // ----------------------------------
12617     // 3. Copy present boundary elements
12618     // ----------------------------------
12619     if ( toCopyExistingBoundary )
12620       for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12621       {
12622         const SMDS_MeshElement* e = presentBndElems[i];
12623         tgtNodes.resize( e->NbNodes() );
12624         for ( inode = 0; inode < tgtNodes.size(); ++inode )
12625           tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12626         presentEditor->AddElement( tgtNodes, elemToCopy.Init( e ));
12627       }
12628     else // store present elements to add them to a group
12629       for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12630       {
12631         presentEditor->myLastCreatedElems.push_back( presentBndElems[ i ]);
12632       }
12633
12634   } // loop on given elements
12635
12636   // ---------------------------------------------
12637   // 4. Fill group with boundary elements
12638   // ---------------------------------------------
12639   if ( group )
12640   {
12641     if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12642       for ( size_t i = 0; i < tgtEditor.myLastCreatedElems.size(); ++i )
12643         g->SMDSGroup().Add( tgtEditor.myLastCreatedElems[ i ]);
12644   }
12645   tgtEditor.myLastCreatedElems.clear();
12646   tgtEditor2.myLastCreatedElems.clear();
12647
12648   // -----------------------
12649   // 5. Copy given elements
12650   // -----------------------
12651   if ( toCopyElements && targetMesh != myMesh )
12652   {
12653     if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12654     else                  eIt = SMESHUtils::elemSetIterator( elements );
12655     while (eIt->more())
12656     {
12657       const SMDS_MeshElement* elem = eIt->next();
12658       tgtNodes.resize( elem->NbNodes() );
12659       for ( inode = 0; inode < tgtNodes.size(); ++inode )
12660         tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12661       tgtEditor.AddElement( tgtNodes, elemToCopy.Init( elem ));
12662
12663       tgtEditor.myLastCreatedElems.clear();
12664     }
12665   }
12666   return nbAddedBnd;
12667 }
12668
12669 //================================================================================
12670 /*!
12671  * \brief Copy node position and set \a to node on the same geometry
12672  */
12673 //================================================================================
12674
12675 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
12676                                      const SMDS_MeshNode* to )
12677 {
12678   if ( !from || !to ) return;
12679
12680   SMDS_PositionPtr pos = from->GetPosition();
12681   if ( !pos || from->getshapeId() < 1 ) return;
12682
12683   switch ( pos->GetTypeOfPosition() )
12684   {
12685   case SMDS_TOP_3DSPACE: break;
12686
12687   case SMDS_TOP_FACE:
12688   {
12689     SMDS_FacePositionPtr fPos = pos;
12690     GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
12691                                 fPos->GetUParameter(), fPos->GetVParameter() );
12692     break;
12693   }
12694   case SMDS_TOP_EDGE:
12695   {
12696     // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
12697     SMDS_EdgePositionPtr ePos = pos;
12698     GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
12699     break;
12700   }
12701   case SMDS_TOP_VERTEX:
12702   {
12703     GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
12704     break;
12705   }
12706   case SMDS_TOP_UNSPEC:
12707   default:;
12708   }
12709 }