Salome HOME
Typo-fix by Kunda + fix user doc generation
[modules/smesh.git] / src / SMESH / SMESH_MeshEditor.cxx
1 // Copyright (C) 2007-2016  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 #include <OSD_Parallel.hxx>
101
102 #include "SMESH_TryCatch.hxx" // include after OCCT headers!
103
104 #define cast2Node(elem) static_cast<const SMDS_MeshNode*>( elem )
105
106 using namespace std;
107 using namespace SMESH::Controls;
108
109 //=======================================================================
110 //function : SMESH_MeshEditor
111 //purpose  :
112 //=======================================================================
113
114 SMESH_MeshEditor::SMESH_MeshEditor( SMESH_Mesh* theMesh )
115   :myMesh( theMesh ) // theMesh may be NULL
116 {
117 }
118
119 //================================================================================
120 /*!
121  * \brief Return mesh DS
122  */
123 //================================================================================
124
125 SMESHDS_Mesh * SMESH_MeshEditor::GetMeshDS()
126 {
127   return myMesh->GetMeshDS();
128 }
129
130
131 //================================================================================
132 /*!
133  * \brief Clears myLastCreatedNodes and myLastCreatedElems
134  */
135 //================================================================================
136
137 void SMESH_MeshEditor::ClearLastCreated()
138 {
139   SMESHUtils::FreeVector( myLastCreatedElems );
140   SMESHUtils::FreeVector( myLastCreatedNodes );
141 }
142
143 //================================================================================
144 /*!
145  * \brief Initializes members by an existing element
146  *  \param [in] elem - the source element
147  *  \param [in] basicOnly - if true, does not set additional data of Ball and Polyhedron
148  */
149 //================================================================================
150
151 SMESH_MeshEditor::ElemFeatures&
152 SMESH_MeshEditor::ElemFeatures::Init( const SMDS_MeshElement* elem, bool basicOnly )
153 {
154   if ( elem )
155   {
156     myType = elem->GetType();
157     if ( myType == SMDSAbs_Face || myType == SMDSAbs_Volume )
158     {
159       myIsPoly = elem->IsPoly();
160       if ( myIsPoly )
161       {
162         myIsQuad = elem->IsQuadratic();
163         if ( myType == SMDSAbs_Volume && !basicOnly )
164         {
165           vector<int> quant = static_cast<const SMDS_MeshVolume* >( elem )->GetQuantities();
166           myPolyhedQuantities.swap( quant );
167         }
168       }
169     }
170     else if ( myType == SMDSAbs_Ball && !basicOnly )
171     {
172       myBallDiameter = static_cast<const SMDS_BallElement*>(elem)->GetDiameter();
173     }
174   }
175   return *this;
176 }
177
178 //=======================================================================
179 /*!
180  * \brief Add element
181  */
182 //=======================================================================
183
184 SMDS_MeshElement*
185 SMESH_MeshEditor::AddElement(const vector<const SMDS_MeshNode*> & node,
186                              const ElemFeatures&                  features)
187 {
188   SMDS_MeshElement* e = 0;
189   int nbnode = node.size();
190   SMESHDS_Mesh* mesh = GetMeshDS();
191   const int ID = features.myID;
192
193   switch ( features.myType ) {
194   case SMDSAbs_Face:
195     if ( !features.myIsPoly ) {
196       if      (nbnode == 3) {
197         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], ID);
198         else           e = mesh->AddFace      (node[0], node[1], node[2] );
199       }
200       else if (nbnode == 4) {
201         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], ID);
202         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3] );
203       }
204       else if (nbnode == 6) {
205         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
206                                                node[4], node[5], ID);
207         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
208                                                node[4], node[5] );
209       }
210       else if (nbnode == 7) {
211         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
212                                                node[4], node[5], node[6], ID);
213         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
214                                                node[4], node[5], node[6] );
215       }
216       else if (nbnode == 8) {
217         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
218                                                node[4], node[5], node[6], node[7], ID);
219         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
220                                                node[4], node[5], node[6], node[7] );
221       }
222       else if (nbnode == 9) {
223         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
224                                                node[4], node[5], node[6], node[7], node[8], ID);
225         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
226                                                node[4], node[5], node[6], node[7], node[8] );
227       }
228     }
229     else if ( !features.myIsQuad )
230     {
231       if ( ID >= 1 ) e = mesh->AddPolygonalFaceWithID(node, ID);
232       else           e = mesh->AddPolygonalFace      (node    );
233     }
234     else if ( nbnode % 2 == 0 ) // just a protection
235     {
236       if ( ID >= 1 ) e = mesh->AddQuadPolygonalFaceWithID(node, ID);
237       else           e = mesh->AddQuadPolygonalFace      (node    );
238     }
239     break;
240
241   case SMDSAbs_Volume:
242     if ( !features.myIsPoly ) {
243       if      (nbnode == 4) {
244         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], ID);
245         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3] );
246       }
247       else if (nbnode == 5) {
248         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
249                                                  node[4], ID);
250         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
251                                                  node[4] );
252       }
253       else if (nbnode == 6) {
254         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
255                                                  node[4], node[5], ID);
256         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
257                                                  node[4], node[5] );
258       }
259       else if (nbnode == 8) {
260         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
261                                                  node[4], node[5], node[6], node[7], ID);
262         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
263                                                  node[4], node[5], node[6], node[7] );
264       }
265       else if (nbnode == 10) {
266         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
267                                                  node[4], node[5], node[6], node[7],
268                                                  node[8], node[9], ID);
269         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
270                                                  node[4], node[5], node[6], node[7],
271                                                  node[8], node[9] );
272       }
273       else if (nbnode == 12) {
274         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
275                                                  node[4], node[5], node[6], node[7],
276                                                  node[8], node[9], node[10], node[11], ID);
277         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
278                                                  node[4], node[5], node[6], node[7],
279                                                  node[8], node[9], node[10], node[11] );
280       }
281       else if (nbnode == 13) {
282         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
283                                                  node[4], node[5], node[6], node[7],
284                                                  node[8], node[9], node[10],node[11],
285                                                  node[12],ID);
286         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
287                                                  node[4], node[5], node[6], node[7],
288                                                  node[8], node[9], node[10],node[11],
289                                                  node[12] );
290       }
291       else if (nbnode == 15) {
292         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
293                                                  node[4], node[5], node[6], node[7],
294                                                  node[8], node[9], node[10],node[11],
295                                                  node[12],node[13],node[14],ID);
296         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
297                                                  node[4], node[5], node[6], node[7],
298                                                  node[8], node[9], node[10],node[11],
299                                                  node[12],node[13],node[14] );
300       }
301       else if (nbnode == 20) {
302         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
303                                                  node[4], node[5], node[6], node[7],
304                                                  node[8], node[9], node[10],node[11],
305                                                  node[12],node[13],node[14],node[15],
306                                                  node[16],node[17],node[18],node[19],ID);
307         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
308                                                  node[4], node[5], node[6], node[7],
309                                                  node[8], node[9], node[10],node[11],
310                                                  node[12],node[13],node[14],node[15],
311                                                  node[16],node[17],node[18],node[19] );
312       }
313       else if (nbnode == 27) {
314         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
315                                                  node[4], node[5], node[6], node[7],
316                                                  node[8], node[9], node[10],node[11],
317                                                  node[12],node[13],node[14],node[15],
318                                                  node[16],node[17],node[18],node[19],
319                                                  node[20],node[21],node[22],node[23],
320                                                  node[24],node[25],node[26], ID);
321         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
322                                                  node[4], node[5], node[6], node[7],
323                                                  node[8], node[9], node[10],node[11],
324                                                  node[12],node[13],node[14],node[15],
325                                                  node[16],node[17],node[18],node[19],
326                                                  node[20],node[21],node[22],node[23],
327                                                  node[24],node[25],node[26] );
328       }
329     }
330     else if ( !features.myIsQuad )
331     {
332       if ( ID >= 1 ) e = mesh->AddPolyhedralVolumeWithID(node, features.myPolyhedQuantities, ID);
333       else           e = mesh->AddPolyhedralVolume      (node, features.myPolyhedQuantities    );
334     }
335     else
336     {
337       // if ( ID >= 1 ) e = mesh->AddQuadPolyhedralVolumeWithID(node, features.myPolyhedQuantities,ID);
338       // else           e = mesh->AddQuadPolyhedralVolume      (node, features.myPolyhedQuantities   );
339     }
340     break;
341
342   case SMDSAbs_Edge:
343     if ( nbnode == 2 ) {
344       if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], ID);
345       else           e = mesh->AddEdge      (node[0], node[1] );
346     }
347     else if ( nbnode == 3 ) {
348       if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], node[2], ID);
349       else           e = mesh->AddEdge      (node[0], node[1], node[2] );
350     }
351     break;
352
353   case SMDSAbs_0DElement:
354     if ( nbnode == 1 ) {
355       if ( ID >= 1 ) e = mesh->Add0DElementWithID(node[0], ID);
356       else           e = mesh->Add0DElement      (node[0] );
357     }
358     break;
359
360   case SMDSAbs_Node:
361     if ( ID >= 1 ) e = mesh->AddNodeWithID(node[0]->X(), node[0]->Y(), node[0]->Z(), ID);
362     else           e = mesh->AddNode      (node[0]->X(), node[0]->Y(), node[0]->Z()    );
363     break;
364
365   case SMDSAbs_Ball:
366     if ( ID >= 1 ) e = mesh->AddBallWithID(node[0], features.myBallDiameter, ID);
367     else           e = mesh->AddBall      (node[0], features.myBallDiameter    );
368     break;
369
370   default:;
371   }
372   if ( e ) myLastCreatedElems.push_back( e );
373   return e;
374 }
375
376 //=======================================================================
377 /*!
378  * \brief Add element
379  */
380 //=======================================================================
381
382 SMDS_MeshElement* SMESH_MeshEditor::AddElement(const vector<int> & nodeIDs,
383                                                const ElemFeatures& features)
384 {
385   vector<const SMDS_MeshNode*> nodes;
386   nodes.reserve( nodeIDs.size() );
387   vector<int>::const_iterator id = nodeIDs.begin();
388   while ( id != nodeIDs.end() ) {
389     if ( const SMDS_MeshNode* node = GetMeshDS()->FindNode( *id++ ))
390       nodes.push_back( node );
391     else
392       return 0;
393   }
394   return AddElement( nodes, features );
395 }
396
397 //=======================================================================
398 //function : Remove
399 //purpose  : Remove a node or an element.
400 //           Modify a compute state of sub-meshes which become empty
401 //=======================================================================
402
403 int SMESH_MeshEditor::Remove (const list< int >& theIDs,
404                               const bool         isNodes )
405 {
406   ClearLastCreated();
407
408   SMESHDS_Mesh* aMesh = GetMeshDS();
409   set< SMESH_subMesh *> smmap;
410
411   int removed = 0;
412   list<int>::const_iterator it = theIDs.begin();
413   for ( ; it != theIDs.end(); it++ ) {
414     const SMDS_MeshElement * elem;
415     if ( isNodes )
416       elem = aMesh->FindNode( *it );
417     else
418       elem = aMesh->FindElement( *it );
419     if ( !elem )
420       continue;
421
422     // Notify VERTEX sub-meshes about modification
423     if ( isNodes ) {
424       const SMDS_MeshNode* node = cast2Node( elem );
425       if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX )
426         if ( int aShapeID = node->getshapeId() )
427           if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
428             smmap.insert( sm );
429     }
430     // Find sub-meshes to notify about modification
431     //     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
432     //     while ( nodeIt->more() ) {
433     //       const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
434     //       const SMDS_PositionPtr& aPosition = node->GetPosition();
435     //       if ( aPosition.get() ) {
436     //         if ( int aShapeID = aPosition->GetShapeId() ) {
437     //           if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
438     //             smmap.insert( sm );
439     //         }
440     //       }
441     //     }
442
443     // Do remove
444     if ( isNodes )
445       aMesh->RemoveNode( static_cast< const SMDS_MeshNode* >( elem ));
446     else
447       aMesh->RemoveElement( elem );
448     removed++;
449   }
450
451   // Notify sub-meshes about modification
452   if ( !smmap.empty() ) {
453     set< SMESH_subMesh *>::iterator smIt;
454     for ( smIt = smmap.begin(); smIt != smmap.end(); smIt++ )
455       (*smIt)->ComputeStateEngine( SMESH_subMesh::MESH_ENTITY_REMOVED );
456   }
457
458   //   // Check if the whole mesh becomes empty
459   //   if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( 1 ) )
460   //     sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
461
462   return removed;
463 }
464
465 //================================================================================
466 /*!
467  * \brief Create 0D elements on all nodes of the given object.
468  *  \param elements - Elements on whose nodes to create 0D elements; if empty, 
469  *                    the all mesh is treated
470  *  \param all0DElems - returns all 0D elements found or created on nodes of \a elements
471  *  \param duplicateElements - to add one more 0D element to a node or not
472  */
473 //================================================================================
474
475 void SMESH_MeshEditor::Create0DElementsOnAllNodes( const TIDSortedElemSet& elements,
476                                                    TIDSortedElemSet&       all0DElems,
477                                                    const bool              duplicateElements )
478 {
479   SMDS_ElemIteratorPtr elemIt;
480   if ( elements.empty() )
481   {
482     elemIt = GetMeshDS()->elementsIterator( SMDSAbs_Node );
483   }
484   else
485   {
486     elemIt = SMESHUtils::elemSetIterator( elements );
487   }
488
489   while ( elemIt->more() )
490   {
491     const SMDS_MeshElement* e = elemIt->next();
492     SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
493     while ( nodeIt->more() )
494     {
495       const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
496       SMDS_ElemIteratorPtr it0D = n->GetInverseElementIterator( SMDSAbs_0DElement );
497       if ( duplicateElements || !it0D->more() )
498       {
499         myLastCreatedElems.push_back( GetMeshDS()->Add0DElement( n ));
500         all0DElems.insert( myLastCreatedElems.back() );
501       }
502       while ( it0D->more() )
503         all0DElems.insert( it0D->next() );
504     }
505   }
506 }
507
508 //=======================================================================
509 //function : FindShape
510 //purpose  : Return an index of the shape theElem is on
511 //           or zero if a shape not found
512 //=======================================================================
513
514 int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem)
515 {
516   ClearLastCreated();
517
518   SMESHDS_Mesh * aMesh = GetMeshDS();
519   if ( aMesh->ShapeToMesh().IsNull() )
520     return 0;
521
522   int aShapeID = theElem->getshapeId();
523   if ( aShapeID < 1 )
524     return 0;
525
526   if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ))
527     if ( sm->Contains( theElem ))
528       return aShapeID;
529
530   if ( theElem->GetType() == SMDSAbs_Node ) {
531     MESSAGE( ":( Error: invalid myShapeId of node " << theElem->GetID() );
532   }
533   else {
534     MESSAGE( ":( Error: invalid myShapeId of element " << theElem->GetID() );
535   }
536
537   TopoDS_Shape aShape; // the shape a node of theElem is on
538   if ( theElem->GetType() != SMDSAbs_Node )
539   {
540     SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
541     while ( nodeIt->more() ) {
542       const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
543       if ((aShapeID = node->getshapeId()) > 0) {
544         if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ) ) {
545           if ( sm->Contains( theElem ))
546             return aShapeID;
547           if ( aShape.IsNull() )
548             aShape = aMesh->IndexToShape( aShapeID );
549         }
550       }
551     }
552   }
553
554   // None of nodes is on a proper shape,
555   // find the shape among ancestors of aShape on which a node is
556   if ( !aShape.IsNull() ) {
557     TopTools_ListIteratorOfListOfShape ancIt( GetMesh()->GetAncestors( aShape ));
558     for ( ; ancIt.More(); ancIt.Next() ) {
559       SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() );
560       if ( sm && sm->Contains( theElem ))
561         return aMesh->ShapeToIndex( ancIt.Value() );
562     }
563   }
564   else
565   {
566     SMESHDS_SubMeshIteratorPtr smIt = GetMeshDS()->SubMeshes();
567     while ( const SMESHDS_SubMesh* sm = smIt->next() )
568       if ( sm->Contains( theElem ))
569         return sm->GetID();
570   }
571
572   return 0;
573 }
574
575 //=======================================================================
576 //function : IsMedium
577 //purpose  :
578 //=======================================================================
579
580 bool SMESH_MeshEditor::IsMedium(const SMDS_MeshNode*      node,
581                                 const SMDSAbs_ElementType typeToCheck)
582 {
583   bool isMedium = false;
584   SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(typeToCheck);
585   while (it->more() && !isMedium ) {
586     const SMDS_MeshElement* elem = it->next();
587     isMedium = elem->IsMediumNode(node);
588   }
589   return isMedium;
590 }
591
592 //=======================================================================
593 //function : shiftNodesQuadTria
594 //purpose  : Shift nodes in the array corresponded to quadratic triangle
595 //           example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
596 //=======================================================================
597
598 static void shiftNodesQuadTria(vector< const SMDS_MeshNode* >& aNodes)
599 {
600   const SMDS_MeshNode* nd1 = aNodes[0];
601   aNodes[0] = aNodes[1];
602   aNodes[1] = aNodes[2];
603   aNodes[2] = nd1;
604   const SMDS_MeshNode* nd2 = aNodes[3];
605   aNodes[3] = aNodes[4];
606   aNodes[4] = aNodes[5];
607   aNodes[5] = nd2;
608 }
609
610 //=======================================================================
611 //function : nbEdgeConnectivity
612 //purpose  : return number of the edges connected with the theNode.
613 //           if theEdges has connections with the other type of the
614 //           elements, return -1
615 //=======================================================================
616
617 static int nbEdgeConnectivity(const SMDS_MeshNode* theNode)
618 {
619   // SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator();
620   // int nb=0;
621   // while(elemIt->more()) {
622   //   elemIt->next();
623   //   nb++;
624   // }
625   // return nb;
626   return theNode->NbInverseElements();
627 }
628
629 //=======================================================================
630 //function : getNodesFromTwoTria
631 //purpose  : 
632 //=======================================================================
633
634 static bool getNodesFromTwoTria(const SMDS_MeshElement * theTria1,
635                                 const SMDS_MeshElement * theTria2,
636                                 vector< const SMDS_MeshNode*>& N1,
637                                 vector< const SMDS_MeshNode*>& N2)
638 {
639   N1.assign( theTria1->begin_nodes(), theTria1->end_nodes() );
640   if ( N1.size() < 6 ) return false;
641   N2.assign( theTria2->begin_nodes(), theTria2->end_nodes() );
642   if ( N2.size() < 6 ) return false;
643
644   int sames[3] = {-1,-1,-1};
645   int nbsames = 0;
646   int i, j;
647   for(i=0; i<3; i++) {
648     for(j=0; j<3; j++) {
649       if(N1[i]==N2[j]) {
650         sames[i] = j;
651         nbsames++;
652         break;
653       }
654     }
655   }
656   if(nbsames!=2) return false;
657   if(sames[0]>-1) {
658     shiftNodesQuadTria(N1);
659     if(sames[1]>-1) {
660       shiftNodesQuadTria(N1);
661     }
662   }
663   i = sames[0] + sames[1] + sames[2];
664   for(; i<2; i++) {
665     shiftNodesQuadTria(N2);
666   }
667   // now we receive following N1 and N2 (using numeration as in the image below)
668   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
669   // i.e. first nodes from both arrays form a new diagonal
670   return true;
671 }
672
673 //=======================================================================
674 //function : InverseDiag
675 //purpose  : Replace two neighbour triangles with ones built on the same 4 nodes
676 //           but having other common link.
677 //           Return False if args are improper
678 //=======================================================================
679
680 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1,
681                                     const SMDS_MeshElement * theTria2 )
682 {
683   ClearLastCreated();
684
685   if ( !theTria1 || !theTria2 ||
686        !dynamic_cast<const SMDS_MeshCell*>( theTria1 ) ||
687        !dynamic_cast<const SMDS_MeshCell*>( theTria2 ) ||
688        theTria1->GetType() != SMDSAbs_Face ||
689        theTria2->GetType() != SMDSAbs_Face )
690     return false;
691
692   if ((theTria1->GetEntityType() == SMDSEntity_Triangle) &&
693       (theTria2->GetEntityType() == SMDSEntity_Triangle))
694   {
695     //  1 +--+ A  theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
696     //    | /|    theTria2: ( B A 2 ) B->1 ( 1 A 2 )   |\ |
697     //    |/ |                                         | \|
698     //  B +--+ 2                                     B +--+ 2
699
700     // put nodes in array and find out indices of the same ones
701     const SMDS_MeshNode* aNodes [6];
702     int sameInd [] = { -1, -1, -1, -1, -1, -1 };
703     int i = 0;
704     SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
705     while ( it->more() ) {
706       aNodes[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
707
708       if ( i > 2 ) // theTria2
709         // find same node of theTria1
710         for ( int j = 0; j < 3; j++ )
711           if ( aNodes[ i ] == aNodes[ j ]) {
712             sameInd[ j ] = i;
713             sameInd[ i ] = j;
714             break;
715           }
716       // next
717       i++;
718       if ( i == 3 ) {
719         if ( it->more() )
720           return false; // theTria1 is not a triangle
721         it = theTria2->nodesIterator();
722       }
723       if ( i == 6 && it->more() )
724         return false; // theTria2 is not a triangle
725     }
726
727     // find indices of 1,2 and of A,B in theTria1
728     int iA = -1, iB = 0, i1 = 0, i2 = 0;
729     for ( i = 0; i < 6; i++ ) {
730       if ( sameInd [ i ] == -1 ) {
731         if ( i < 3 ) i1 = i;
732         else         i2 = i;
733       }
734       else if (i < 3) {
735         if ( iA >= 0) iB = i;
736         else          iA = i;
737       }
738     }
739     // nodes 1 and 2 should not be the same
740     if ( aNodes[ i1 ] == aNodes[ i2 ] )
741       return false;
742
743     // theTria1: A->2
744     aNodes[ iA ] = aNodes[ i2 ];
745     // theTria2: B->1
746     aNodes[ sameInd[ iB ]] = aNodes[ i1 ];
747
748     GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 );
749     GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 );
750
751     return true;
752
753   } // end if(F1 && F2)
754
755   // check case of quadratic faces
756   if (theTria1->GetEntityType() != SMDSEntity_Quad_Triangle &&
757       theTria1->GetEntityType() != SMDSEntity_BiQuad_Triangle)
758     return false;
759   if (theTria2->GetEntityType() != SMDSEntity_Quad_Triangle&&
760       theTria2->GetEntityType() != SMDSEntity_BiQuad_Triangle)
761     return false;
762
763   //       5
764   //  1 +--+--+ 2  theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
765   //    |    /|    theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
766   //    |   / |
767   //  7 +  +  + 6
768   //    | /9  |
769   //    |/    |
770   //  4 +--+--+ 3
771   //       8
772
773   vector< const SMDS_MeshNode* > N1;
774   vector< const SMDS_MeshNode* > N2;
775   if(!getNodesFromTwoTria(theTria1,theTria2,N1,N2))
776     return false;
777   // now we receive following N1 and N2 (using numeration as above image)
778   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
779   // i.e. first nodes from both arrays determ new diagonal
780
781   vector< const SMDS_MeshNode*> N1new( N1.size() );
782   vector< const SMDS_MeshNode*> N2new( N2.size() );
783   N1new.back() = N1.back(); // central node of biquadratic
784   N2new.back() = N2.back();
785   N1new[0] = N1[0];  N2new[0] = N1[0];
786   N1new[1] = N2[0];  N2new[1] = N1[1];
787   N1new[2] = N2[1];  N2new[2] = N2[0];
788   N1new[3] = N1[4];  N2new[3] = N1[3];
789   N1new[4] = N2[3];  N2new[4] = N2[5];
790   N1new[5] = N1[5];  N2new[5] = N1[4];
791   // change nodes in faces
792   GetMeshDS()->ChangeElementNodes( theTria1, &N1new[0], N1new.size() );
793   GetMeshDS()->ChangeElementNodes( theTria2, &N2new[0], N2new.size() );
794
795   // move the central node of biquadratic triangle
796   SMESH_MesherHelper helper( *GetMesh() );
797   for ( int is2nd = 0; is2nd < 2; ++is2nd )
798   {
799     const SMDS_MeshElement*         tria = is2nd ? theTria2 : theTria1;
800     vector< const SMDS_MeshNode*>& nodes = is2nd ? N2new : N1new;
801     if ( nodes.size() < 7 )
802       continue;
803     helper.SetSubShape( tria->getshapeId() );
804     const TopoDS_Face& F = TopoDS::Face( helper.GetSubShape() );
805     gp_Pnt xyz;
806     if ( F.IsNull() )
807     {
808       xyz = ( SMESH_NodeXYZ( nodes[3] ) +
809               SMESH_NodeXYZ( nodes[4] ) +
810               SMESH_NodeXYZ( nodes[5] )) / 3.;
811     }
812     else
813     {
814       bool checkUV;
815       gp_XY uv = ( helper.GetNodeUV( F, nodes[3], nodes[2], &checkUV ) +
816                    helper.GetNodeUV( F, nodes[4], nodes[0], &checkUV ) +
817                    helper.GetNodeUV( F, nodes[5], nodes[1], &checkUV )) / 3.;
818       TopLoc_Location loc;
819       Handle(Geom_Surface) S = BRep_Tool::Surface(F,loc);
820       xyz = S->Value( uv.X(), uv.Y() );
821       xyz.Transform( loc );
822       if ( nodes[6]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE &&  // set UV
823            nodes[6]->getshapeId() > 0 )
824         GetMeshDS()->SetNodeOnFace( nodes[6], nodes[6]->getshapeId(), uv.X(), uv.Y() );
825     }
826     GetMeshDS()->MoveNode( nodes[6], xyz.X(), xyz.Y(), xyz.Z() );
827   }
828   return true;
829 }
830
831 //=======================================================================
832 //function : findTriangles
833 //purpose  : find triangles sharing theNode1-theNode2 link
834 //=======================================================================
835
836 static bool findTriangles(const SMDS_MeshNode *    theNode1,
837                           const SMDS_MeshNode *    theNode2,
838                           const SMDS_MeshElement*& theTria1,
839                           const SMDS_MeshElement*& theTria2)
840 {
841   if ( !theNode1 || !theNode2 ) return false;
842
843   theTria1 = theTria2 = 0;
844
845   set< const SMDS_MeshElement* > emap;
846   SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face);
847   while (it->more()) {
848     const SMDS_MeshElement* elem = it->next();
849     if ( elem->NbCornerNodes() == 3 )
850       emap.insert( elem );
851   }
852   it = theNode2->GetInverseElementIterator(SMDSAbs_Face);
853   while (it->more()) {
854     const SMDS_MeshElement* elem = it->next();
855     if ( emap.count( elem )) {
856       if ( !theTria1 )
857       {
858         theTria1 = elem;
859       }
860       else  
861       {
862         theTria2 = elem;
863         // theTria1 must be element with minimum ID
864         if ( theTria2->GetID() < theTria1->GetID() )
865           std::swap( theTria2, theTria1 );
866         return true;
867       }
868     }
869   }
870   return false;
871 }
872
873 //=======================================================================
874 //function : InverseDiag
875 //purpose  : Replace two neighbour triangles sharing theNode1-theNode2 link
876 //           with ones built on the same 4 nodes but having other common link.
877 //           Return false if proper faces not found
878 //=======================================================================
879
880 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
881                                     const SMDS_MeshNode * theNode2)
882 {
883   ClearLastCreated();
884
885   const SMDS_MeshElement *tr1, *tr2;
886   if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
887     return false;
888
889   if ( !dynamic_cast<const SMDS_MeshCell*>( tr1 ) ||
890        !dynamic_cast<const SMDS_MeshCell*>( tr2 ))
891     return false;
892
893   if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
894       (tr2->GetEntityType() == SMDSEntity_Triangle)) {
895
896     //  1 +--+ A  tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
897     //    | /|    tr2: ( B A 2 ) B->1 ( 1 A 2 )   |\ |
898     //    |/ |                                    | \|
899     //  B +--+ 2                                B +--+ 2
900
901     // put nodes in array
902     // and find indices of 1,2 and of A in tr1 and of B in tr2
903     int i, iA1 = 0, i1 = 0;
904     const SMDS_MeshNode* aNodes1 [3];
905     SMDS_ElemIteratorPtr it;
906     for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) {
907       aNodes1[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
908       if ( aNodes1[ i ] == theNode1 )
909         iA1 = i; // node A in tr1
910       else if ( aNodes1[ i ] != theNode2 )
911         i1 = i;  // node 1
912     }
913     int iB2 = 0, i2 = 0;
914     const SMDS_MeshNode* aNodes2 [3];
915     for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) {
916       aNodes2[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
917       if ( aNodes2[ i ] == theNode2 )
918         iB2 = i; // node B in tr2
919       else if ( aNodes2[ i ] != theNode1 )
920         i2 = i;  // node 2
921     }
922
923     // nodes 1 and 2 should not be the same
924     if ( aNodes1[ i1 ] == aNodes2[ i2 ] )
925       return false;
926
927     // tr1: A->2
928     aNodes1[ iA1 ] = aNodes2[ i2 ];
929     // tr2: B->1
930     aNodes2[ iB2 ] = aNodes1[ i1 ];
931
932     GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 );
933     GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 );
934
935     return true;
936   }
937
938   // check case of quadratic faces
939   return InverseDiag(tr1,tr2);
940 }
941
942 //=======================================================================
943 //function : getQuadrangleNodes
944 //purpose  : fill theQuadNodes - nodes of a quadrangle resulting from
945 //           fusion of triangles tr1 and tr2 having shared link on
946 //           theNode1 and theNode2
947 //=======================================================================
948
949 bool getQuadrangleNodes(const SMDS_MeshNode *    theQuadNodes [],
950                         const SMDS_MeshNode *    theNode1,
951                         const SMDS_MeshNode *    theNode2,
952                         const SMDS_MeshElement * tr1,
953                         const SMDS_MeshElement * tr2 )
954 {
955   if( tr1->NbNodes() != tr2->NbNodes() )
956     return false;
957   // find the 4-th node to insert into tr1
958   const SMDS_MeshNode* n4 = 0;
959   SMDS_ElemIteratorPtr it = tr2->nodesIterator();
960   int i=0;
961   while ( !n4 && i<3 ) {
962     const SMDS_MeshNode * n = cast2Node( it->next() );
963     i++;
964     bool isDiag = ( n == theNode1 || n == theNode2 );
965     if ( !isDiag )
966       n4 = n;
967   }
968   // Make an array of nodes to be in a quadrangle
969   int iNode = 0, iFirstDiag = -1;
970   it = tr1->nodesIterator();
971   i=0;
972   while ( i<3 ) {
973     const SMDS_MeshNode * n = cast2Node( it->next() );
974     i++;
975     bool isDiag = ( n == theNode1 || n == theNode2 );
976     if ( isDiag ) {
977       if ( iFirstDiag < 0 )
978         iFirstDiag = iNode;
979       else if ( iNode - iFirstDiag == 1 )
980         theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes
981     }
982     else if ( n == n4 ) {
983       return false; // tr1 and tr2 should not have all the same nodes
984     }
985     theQuadNodes[ iNode++ ] = n;
986   }
987   if ( iNode == 3 ) // diagonal nodes have 0 and 2 indices
988     theQuadNodes[ iNode ] = n4;
989
990   return true;
991 }
992
993 //=======================================================================
994 //function : DeleteDiag
995 //purpose  : Replace two neighbour triangles sharing theNode1-theNode2 link
996 //           with a quadrangle built on the same 4 nodes.
997 //           Return false if proper faces not found
998 //=======================================================================
999
1000 bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
1001                                    const SMDS_MeshNode * theNode2)
1002 {
1003   ClearLastCreated();
1004
1005   const SMDS_MeshElement *tr1, *tr2;
1006   if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
1007     return false;
1008
1009   if ( !dynamic_cast<const SMDS_MeshCell*>( tr1 ) ||
1010        !dynamic_cast<const SMDS_MeshCell*>( tr2 ))
1011     return false;
1012
1013   SMESHDS_Mesh * aMesh = GetMeshDS();
1014
1015   if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
1016       (tr2->GetEntityType() == SMDSEntity_Triangle))
1017   {
1018     const SMDS_MeshNode* aNodes [ 4 ];
1019     if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 ))
1020       return false;
1021
1022     const SMDS_MeshElement* newElem = 0;
1023     newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3] );
1024     myLastCreatedElems.push_back(newElem);
1025     AddToSameGroups( newElem, tr1, aMesh );
1026     int aShapeId = tr1->getshapeId();
1027     if ( aShapeId )
1028       aMesh->SetMeshElementOnShape( newElem, aShapeId );
1029
1030     aMesh->RemoveElement( tr1 );
1031     aMesh->RemoveElement( tr2 );
1032
1033     return true;
1034   }
1035
1036   // check case of quadratic faces
1037   if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
1038     return false;
1039   if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
1040     return false;
1041
1042   //       5
1043   //  1 +--+--+ 2  tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
1044   //    |    /|    tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
1045   //    |   / |
1046   //  7 +  +  + 6
1047   //    | /9  |
1048   //    |/    |
1049   //  4 +--+--+ 3
1050   //       8
1051
1052   vector< const SMDS_MeshNode* > N1;
1053   vector< const SMDS_MeshNode* > N2;
1054   if(!getNodesFromTwoTria(tr1,tr2,N1,N2))
1055     return false;
1056   // now we receive following N1 and N2 (using numeration as above image)
1057   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
1058   // i.e. first nodes from both arrays determ new diagonal
1059
1060   const SMDS_MeshNode* aNodes[8];
1061   aNodes[0] = N1[0];
1062   aNodes[1] = N1[1];
1063   aNodes[2] = N2[0];
1064   aNodes[3] = N2[1];
1065   aNodes[4] = N1[3];
1066   aNodes[5] = N2[5];
1067   aNodes[6] = N2[3];
1068   aNodes[7] = N1[5];
1069
1070   const SMDS_MeshElement* newElem = 0;
1071   newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
1072                             aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
1073   myLastCreatedElems.push_back(newElem);
1074   AddToSameGroups( newElem, tr1, aMesh );
1075   int aShapeId = tr1->getshapeId();
1076   if ( aShapeId )
1077   {
1078     aMesh->SetMeshElementOnShape( newElem, aShapeId );
1079   }
1080   aMesh->RemoveElement( tr1 );
1081   aMesh->RemoveElement( tr2 );
1082
1083   // remove middle node (9)
1084   GetMeshDS()->RemoveNode( N1[4] );
1085
1086   return true;
1087 }
1088
1089 //=======================================================================
1090 //function : Reorient
1091 //purpose  : Reverse theElement orientation
1092 //=======================================================================
1093
1094 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
1095 {
1096   ClearLastCreated();
1097
1098   if (!theElem)
1099     return false;
1100   SMDS_ElemIteratorPtr it = theElem->nodesIterator();
1101   if ( !it || !it->more() )
1102     return false;
1103
1104   const SMDSAbs_ElementType type = theElem->GetType();
1105   if ( type < SMDSAbs_Edge || type > SMDSAbs_Volume )
1106     return false;
1107
1108   const SMDSAbs_EntityType geomType = theElem->GetEntityType();
1109   if ( geomType == SMDSEntity_Polyhedra ) // polyhedron
1110   {
1111     const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( theElem );
1112     if (!aPolyedre) {
1113       MESSAGE("Warning: bad volumic element");
1114       return false;
1115     }
1116     const int nbFaces = aPolyedre->NbFaces();
1117     vector<const SMDS_MeshNode *> poly_nodes;
1118     vector<int> quantities (nbFaces);
1119
1120     // reverse each face of the polyedre
1121     for (int iface = 1; iface <= nbFaces; iface++) {
1122       int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
1123       quantities[iface - 1] = nbFaceNodes;
1124
1125       for (inode = nbFaceNodes; inode >= 1; inode--) {
1126         const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
1127         poly_nodes.push_back(curNode);
1128       }
1129     }
1130     return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
1131   }
1132   else // other elements
1133   {
1134     vector<const SMDS_MeshNode*> nodes( theElem->begin_nodes(), theElem->end_nodes() );
1135     const std::vector<int>& interlace = SMDS_MeshCell::reverseSmdsOrder( geomType, nodes.size() );
1136     if ( interlace.empty() )
1137     {
1138       std::reverse( nodes.begin(), nodes.end() ); // obsolete, just in case
1139     }
1140     else
1141     {
1142       SMDS_MeshCell::applyInterlace( interlace, nodes );
1143     }
1144     return GetMeshDS()->ChangeElementNodes( theElem, &nodes[0], nodes.size() );
1145   }
1146   return false;
1147 }
1148
1149 //================================================================================
1150 /*!
1151  * \brief Reorient faces.
1152  * \param theFaces - the faces to reorient. If empty the whole mesh is meant
1153  * \param theDirection - desired direction of normal of \a theFace
1154  * \param theFace - one of \a theFaces that should be oriented according to
1155  *        \a theDirection and whose orientation defines orientation of other faces
1156  * \return number of reoriented faces.
1157  */
1158 //================================================================================
1159
1160 int SMESH_MeshEditor::Reorient2D (TIDSortedElemSet &       theFaces,
1161                                   const gp_Dir&            theDirection,
1162                                   const SMDS_MeshElement * theFace)
1163 {
1164   int nbReori = 0;
1165   if ( !theFace || theFace->GetType() != SMDSAbs_Face ) return nbReori;
1166
1167   if ( theFaces.empty() )
1168   {
1169     SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator(/*idInceasingOrder=true*/);
1170     while ( fIt->more() )
1171       theFaces.insert( theFaces.end(), fIt->next() );
1172   }
1173
1174   // orient theFace according to theDirection
1175   gp_XYZ normal;
1176   SMESH_MeshAlgos::FaceNormal( theFace, normal, /*normalized=*/false );
1177   if ( normal * theDirection.XYZ() < 0 )
1178     nbReori += Reorient( theFace );
1179
1180   // Orient other faces
1181
1182   set< const SMDS_MeshElement* > startFaces, visitedFaces;
1183   TIDSortedElemSet avoidSet;
1184   set< SMESH_TLink > checkedLinks;
1185   pair< set< SMESH_TLink >::iterator, bool > linkIt_isNew;
1186
1187   if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces
1188     theFaces.erase( theFace );
1189   startFaces.insert( theFace );
1190
1191   int nodeInd1, nodeInd2;
1192   const SMDS_MeshElement*           otherFace;
1193   vector< const SMDS_MeshElement* > facesNearLink;
1194   vector< std::pair< int, int > >   nodeIndsOfFace;
1195
1196   set< const SMDS_MeshElement* >::iterator startFace = startFaces.begin();
1197   while ( !startFaces.empty() )
1198   {
1199     startFace = startFaces.begin();
1200     theFace = *startFace;
1201     startFaces.erase( startFace );
1202     if ( !visitedFaces.insert( theFace ).second )
1203       continue;
1204
1205     avoidSet.clear();
1206     avoidSet.insert(theFace);
1207
1208     NLink link( theFace->GetNode( 0 ), (SMDS_MeshNode *) 0 );
1209
1210     const int nbNodes = theFace->NbCornerNodes();
1211     for ( int i = 0; i < nbNodes; ++i ) // loop on links of theFace
1212     {
1213       link.second = theFace->GetNode(( i+1 ) % nbNodes );
1214       linkIt_isNew = checkedLinks.insert( link );
1215       if ( !linkIt_isNew.second )
1216       {
1217         // link has already been checked and won't be encountered more
1218         // if the group (theFaces) is manifold
1219         //checkedLinks.erase( linkIt_isNew.first );
1220       }
1221       else
1222       {
1223         facesNearLink.clear();
1224         nodeIndsOfFace.clear();
1225         while (( otherFace = SMESH_MeshAlgos::FindFaceInSet( link.first, link.second,
1226                                                              theFaces, avoidSet,
1227                                                              &nodeInd1, &nodeInd2 )))
1228           if ( otherFace != theFace)
1229           {
1230             facesNearLink.push_back( otherFace );
1231             nodeIndsOfFace.push_back( make_pair( nodeInd1, nodeInd2 ));
1232             avoidSet.insert( otherFace );
1233           }
1234         if ( facesNearLink.size() > 1 )
1235         {
1236           // NON-MANIFOLD mesh shell !
1237           // select a face most co-directed with theFace,
1238           // other faces won't be visited this time
1239           gp_XYZ NF, NOF;
1240           SMESH_MeshAlgos::FaceNormal( theFace, NF, /*normalized=*/false );
1241           double proj, maxProj = -1;
1242           for ( size_t i = 0; i < facesNearLink.size(); ++i ) {
1243             SMESH_MeshAlgos::FaceNormal( facesNearLink[i], NOF, /*normalized=*/false );
1244             if (( proj = Abs( NF * NOF )) > maxProj ) {
1245               maxProj = proj;
1246               otherFace = facesNearLink[i];
1247               nodeInd1  = nodeIndsOfFace[i].first;
1248               nodeInd2  = nodeIndsOfFace[i].second;
1249             }
1250           }
1251           // not to visit rejected faces
1252           for ( size_t i = 0; i < facesNearLink.size(); ++i )
1253             if ( facesNearLink[i] != otherFace && theFaces.size() > 1 )
1254               visitedFaces.insert( facesNearLink[i] );
1255         }
1256         else if ( facesNearLink.size() == 1 )
1257         {
1258           otherFace = facesNearLink[0];
1259           nodeInd1  = nodeIndsOfFace.back().first;
1260           nodeInd2  = nodeIndsOfFace.back().second;
1261         }
1262         if ( otherFace && otherFace != theFace)
1263         {
1264           // link must be reverse in otherFace if orientation to otherFace
1265           // is same as that of theFace
1266           if ( abs(nodeInd2-nodeInd1) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1267           {
1268             nbReori += Reorient( otherFace );
1269           }
1270           startFaces.insert( otherFace );
1271         }
1272       }
1273       std::swap( link.first, link.second ); // reverse the link
1274     }
1275   }
1276   return nbReori;
1277 }
1278
1279 //================================================================================
1280 /*!
1281  * \brief Reorient faces basing on orientation of adjacent volumes.
1282  * \param theFaces - faces to reorient. If empty, all mesh faces are treated.
1283  * \param theVolumes - reference volumes.
1284  * \param theOutsideNormal - to orient faces to have their normal
1285  *        pointing either \a outside or \a inside the adjacent volumes.
1286  * \return number of reoriented faces.
1287  */
1288 //================================================================================
1289
1290 int SMESH_MeshEditor::Reorient2DBy3D (TIDSortedElemSet & theFaces,
1291                                       TIDSortedElemSet & theVolumes,
1292                                       const bool         theOutsideNormal)
1293 {
1294   int nbReori = 0;
1295
1296   SMDS_ElemIteratorPtr faceIt;
1297   if ( theFaces.empty() )
1298     faceIt = GetMeshDS()->elementsIterator( SMDSAbs_Face );
1299   else
1300     faceIt = SMESHUtils::elemSetIterator( theFaces );
1301
1302   vector< const SMDS_MeshNode* > faceNodes;
1303   TIDSortedElemSet checkedVolumes;
1304   set< const SMDS_MeshNode* > faceNodesSet;
1305   SMDS_VolumeTool volumeTool;
1306
1307   while ( faceIt->more() ) // loop on given faces
1308   {
1309     const SMDS_MeshElement* face = faceIt->next();
1310     if ( face->GetType() != SMDSAbs_Face )
1311       continue;
1312
1313     const size_t nbCornersNodes = face->NbCornerNodes();
1314     faceNodes.assign( face->begin_nodes(), face->end_nodes() );
1315
1316     checkedVolumes.clear();
1317     SMDS_ElemIteratorPtr vIt = faceNodes[ 0 ]->GetInverseElementIterator( SMDSAbs_Volume );
1318     while ( vIt->more() )
1319     {
1320       const SMDS_MeshElement* volume = vIt->next();
1321
1322       if ( !checkedVolumes.insert( volume ).second )
1323         continue;
1324       if ( !theVolumes.empty() && !theVolumes.count( volume ))
1325         continue;
1326
1327       // is volume adjacent?
1328       bool allNodesCommon = true;
1329       for ( size_t iN = 1; iN < nbCornersNodes && allNodesCommon; ++iN )
1330         allNodesCommon = ( volume->GetNodeIndex( faceNodes[ iN ]) > -1 );
1331       if ( !allNodesCommon )
1332         continue;
1333
1334       // get nodes of a corresponding volume facet
1335       faceNodesSet.clear();
1336       faceNodesSet.insert( faceNodes.begin(), faceNodes.end() );
1337       volumeTool.Set( volume );
1338       int facetID = volumeTool.GetFaceIndex( faceNodesSet );
1339       if ( facetID < 0 ) continue;
1340       volumeTool.SetExternalNormal();
1341       const SMDS_MeshNode** facetNodes = volumeTool.GetFaceNodes( facetID );
1342
1343       // compare order of faceNodes and facetNodes
1344       const int iQ = 1 + ( nbCornersNodes < faceNodes.size() );
1345       int iNN[2];
1346       for ( int i = 0; i < 2; ++i )
1347       {
1348         const SMDS_MeshNode* n = facetNodes[ i*iQ ];
1349         for ( size_t iN = 0; iN < nbCornersNodes; ++iN )
1350           if ( faceNodes[ iN ] == n )
1351           {
1352             iNN[ i ] = iN;
1353             break;
1354           }
1355       }
1356       bool isOutside = Abs( iNN[0]-iNN[1] ) == 1 ? iNN[0] < iNN[1] : iNN[0] > iNN[1];
1357       if ( isOutside != theOutsideNormal )
1358         nbReori += Reorient( face );
1359     }
1360   }  // loop on given faces
1361
1362   return nbReori;
1363 }
1364
1365 //=======================================================================
1366 //function : getBadRate
1367 //purpose  :
1368 //=======================================================================
1369
1370 static double getBadRate (const SMDS_MeshElement*               theElem,
1371                           SMESH::Controls::NumericalFunctorPtr& theCrit)
1372 {
1373   SMESH::Controls::TSequenceOfXYZ P;
1374   if ( !theElem || !theCrit->GetPoints( theElem, P ))
1375     return 1e100;
1376   return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1377   //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1378 }
1379
1380 //=======================================================================
1381 //function : QuadToTri
1382 //purpose  : Cut quadrangles into triangles.
1383 //           theCrit is used to select a diagonal to cut
1384 //=======================================================================
1385
1386 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet &                   theElems,
1387                                   SMESH::Controls::NumericalFunctorPtr theCrit)
1388 {
1389   ClearLastCreated();
1390
1391   if ( !theCrit.get() )
1392     return false;
1393
1394   SMESHDS_Mesh *       aMesh = GetMeshDS();
1395   Handle(Geom_Surface) surface;
1396   SMESH_MesherHelper   helper( *GetMesh() );
1397
1398   myLastCreatedElems.reserve( theElems.size() * 2 );
1399
1400   TIDSortedElemSet::iterator itElem;
1401   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
1402   {
1403     const SMDS_MeshElement* elem = *itElem;
1404     if ( !elem || elem->GetType() != SMDSAbs_Face )
1405       continue;
1406     if ( elem->NbCornerNodes() != 4 )
1407       continue;
1408
1409     // retrieve element nodes
1410     vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1411
1412     // compare two sets of possible triangles
1413     double aBadRate1, aBadRate2; // to what extent a set is bad
1414     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1415     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1416     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1417
1418     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1419     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1420     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1421
1422     const int aShapeId = FindShape( elem );
1423     const SMDS_MeshElement* newElem1 = 0;
1424     const SMDS_MeshElement* newElem2 = 0;
1425
1426     if ( !elem->IsQuadratic() ) // split linear quadrangle
1427     {
1428       // for MaxElementLength2D functor we return minimum diagonal for splitting,
1429       // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1430       if ( aBadRate1 <= aBadRate2 ) {
1431         // tr1 + tr2 is better
1432         newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1433         newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1434       }
1435       else {
1436         // tr3 + tr4 is better
1437         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1438         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1439       }
1440     }
1441     else // split quadratic quadrangle
1442     {
1443       helper.SetIsQuadratic( true );
1444       helper.SetIsBiQuadratic( aNodes.size() == 9 );
1445
1446       helper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem ));
1447       if ( aNodes.size() == 9 )
1448       {
1449         helper.SetIsBiQuadratic( true );
1450         if ( aBadRate1 <= aBadRate2 )
1451           helper.AddTLinkNode( aNodes[0], aNodes[2], aNodes[8] );
1452         else
1453           helper.AddTLinkNode( aNodes[1], aNodes[3], aNodes[8] );
1454       }
1455       // create a new element
1456       if ( aBadRate1 <= aBadRate2 ) {
1457         newElem1 = helper.AddFace( aNodes[2], aNodes[3], aNodes[0] );
1458         newElem2 = helper.AddFace( aNodes[2], aNodes[0], aNodes[1] );
1459       }
1460       else {
1461         newElem1 = helper.AddFace( aNodes[3], aNodes[0], aNodes[1] );
1462         newElem2 = helper.AddFace( aNodes[3], aNodes[1], aNodes[2] );
1463       }
1464     } // quadratic case
1465
1466     // care of a new element
1467
1468     myLastCreatedElems.push_back(newElem1);
1469     myLastCreatedElems.push_back(newElem2);
1470     AddToSameGroups( newElem1, elem, aMesh );
1471     AddToSameGroups( newElem2, elem, aMesh );
1472
1473     // put a new triangle on the same shape
1474     if ( aShapeId )
1475       aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1476     aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1477
1478     aMesh->RemoveElement( elem );
1479   }
1480   return true;
1481 }
1482
1483 //=======================================================================
1484 /*!
1485  * \brief Split each of given quadrangles into 4 triangles.
1486  * \param theElems - The faces to be split. If empty all faces are split.
1487  */
1488 //=======================================================================
1489
1490 void SMESH_MeshEditor::QuadTo4Tri (TIDSortedElemSet & theElems)
1491 {
1492   ClearLastCreated();
1493   myLastCreatedElems.reserve( theElems.size() * 4 );
1494
1495   SMESH_MesherHelper helper( *GetMesh() );
1496   helper.SetElementsOnShape( true );
1497
1498   SMDS_ElemIteratorPtr faceIt;
1499   if ( theElems.empty() ) faceIt = GetMeshDS()->elementsIterator(SMDSAbs_Face);
1500   else                    faceIt = SMESHUtils::elemSetIterator( theElems );
1501
1502   bool   checkUV;
1503   gp_XY  uv [9]; uv[8] = gp_XY(0,0);
1504   gp_XYZ xyz[9];
1505   vector< const SMDS_MeshNode* > nodes;
1506   SMESHDS_SubMesh*               subMeshDS = 0;
1507   TopoDS_Face                    F;
1508   Handle(Geom_Surface)           surface;
1509   TopLoc_Location                loc;
1510
1511   while ( faceIt->more() )
1512   {
1513     const SMDS_MeshElement* quad = faceIt->next();
1514     if ( !quad || quad->NbCornerNodes() != 4 )
1515       continue;
1516
1517     // get a surface the quad is on
1518
1519     if ( quad->getshapeId() < 1 )
1520     {
1521       F.Nullify();
1522       helper.SetSubShape( 0 );
1523       subMeshDS = 0;
1524     }
1525     else if ( quad->getshapeId() != helper.GetSubShapeID() )
1526     {
1527       helper.SetSubShape( quad->getshapeId() );
1528       if ( !helper.GetSubShape().IsNull() &&
1529            helper.GetSubShape().ShapeType() == TopAbs_FACE )
1530       {
1531         F = TopoDS::Face( helper.GetSubShape() );
1532         surface = BRep_Tool::Surface( F, loc );
1533         subMeshDS = GetMeshDS()->MeshElements( quad->getshapeId() );
1534       }
1535       else
1536       {
1537         helper.SetSubShape( 0 );
1538         subMeshDS = 0;
1539       }
1540     }
1541
1542     // create a central node
1543
1544     const SMDS_MeshNode* nCentral;
1545     nodes.assign( quad->begin_nodes(), quad->end_nodes() );
1546
1547     if ( nodes.size() == 9 )
1548     {
1549       nCentral = nodes.back();
1550     }
1551     else
1552     {
1553       size_t iN = 0;
1554       if ( F.IsNull() )
1555       {
1556         for ( ; iN < nodes.size(); ++iN )
1557           xyz[ iN ] = SMESH_NodeXYZ( nodes[ iN ] );
1558
1559         for ( ; iN < 8; ++iN ) // mid-side points of a linear qudrangle
1560           xyz[ iN ] = 0.5 * ( xyz[ iN - 4 ] + xyz[( iN - 3 )%4 ] );
1561
1562         xyz[ 8 ] = helper.calcTFI( 0.5, 0.5,
1563                                    xyz[0], xyz[1], xyz[2], xyz[3],
1564                                    xyz[4], xyz[5], xyz[6], xyz[7] );
1565       }
1566       else
1567       {
1568         for ( ; iN < nodes.size(); ++iN )
1569           uv[ iN ] = helper.GetNodeUV( F, nodes[iN], nodes[(iN+2)%4], &checkUV );
1570
1571         for ( ; iN < 8; ++iN ) // UV of mid-side points of a linear qudrangle
1572           uv[ iN ] = helper.GetMiddleUV( surface, uv[ iN - 4 ], uv[( iN - 3 )%4 ] );
1573
1574         uv[ 8 ] = helper.calcTFI( 0.5, 0.5,
1575                                   uv[0], uv[1], uv[2], uv[3],
1576                                   uv[4], uv[5], uv[6], uv[7] );
1577
1578         gp_Pnt p = surface->Value( uv[8].X(), uv[8].Y() ).Transformed( loc );
1579         xyz[ 8 ] = p.XYZ();
1580       }
1581
1582       nCentral = helper.AddNode( xyz[8].X(), xyz[8].Y(), xyz[8].Z(), /*id=*/0,
1583                                  uv[8].X(), uv[8].Y() );
1584       myLastCreatedNodes.push_back( nCentral );
1585     }
1586
1587     // create 4 triangles
1588
1589     helper.SetIsQuadratic  ( nodes.size() > 4 );
1590     helper.SetIsBiQuadratic( nodes.size() == 9 );
1591     if ( helper.GetIsQuadratic() )
1592       helper.AddTLinks( static_cast< const SMDS_MeshFace*>( quad ));
1593
1594     GetMeshDS()->RemoveFreeElement( quad, subMeshDS, /*fromGroups=*/false );
1595
1596     for ( int i = 0; i < 4; ++i )
1597     {
1598       SMDS_MeshElement* tria = helper.AddFace( nodes[ i ],
1599                                                nodes[(i+1)%4],
1600                                                nCentral );
1601       ReplaceElemInGroups( tria, quad, GetMeshDS() );
1602       myLastCreatedElems.push_back( tria );
1603     }
1604   }
1605 }
1606
1607 //=======================================================================
1608 //function : BestSplit
1609 //purpose  : Find better diagonal for cutting.
1610 //=======================================================================
1611
1612 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement*              theQuad,
1613                                  SMESH::Controls::NumericalFunctorPtr theCrit)
1614 {
1615   ClearLastCreated();
1616
1617   if (!theCrit.get())
1618     return -1;
1619
1620   if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1621     return -1;
1622
1623   if( theQuad->NbNodes()==4 ||
1624       (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1625
1626     // retrieve element nodes
1627     const SMDS_MeshNode* aNodes [4];
1628     SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1629     int i = 0;
1630     //while (itN->more())
1631     while (i<4) {
1632       aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1633     }
1634     // compare two sets of possible triangles
1635     double aBadRate1, aBadRate2; // to what extent a set is bad
1636     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1637     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1638     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1639
1640     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1641     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1642     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1643     // for MaxElementLength2D functor we return minimum diagonal for splitting,
1644     // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1645     if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1646       return 1; // diagonal 1-3
1647
1648     return 2; // diagonal 2-4
1649   }
1650   return -1;
1651 }
1652
1653 namespace
1654 {
1655   // Methods of splitting volumes into tetra
1656
1657   const int theHexTo5_1[5*4+1] =
1658     {
1659       0, 1, 2, 5,    0, 4, 5, 7,     0, 2, 3, 7,    2, 5, 6, 7,     0, 5, 2, 7,   -1
1660     };
1661   const int theHexTo5_2[5*4+1] =
1662     {
1663       1, 2, 3, 6,    1, 4, 5, 6,     0, 1, 3, 4,    3, 4, 6, 7,     1, 3, 4, 6,   -1
1664     };
1665   const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1666
1667   const int theHexTo6_1[6*4+1] =
1668     {
1669       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
1670     };
1671   const int theHexTo6_2[6*4+1] =
1672     {
1673       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
1674     };
1675   const int theHexTo6_3[6*4+1] =
1676     {
1677       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
1678     };
1679   const int theHexTo6_4[6*4+1] =
1680     {
1681       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
1682     };
1683   const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1684
1685   const int thePyraTo2_1[2*4+1] =
1686     {
1687       0, 1, 2, 4,    0, 2, 3, 4,   -1
1688     };
1689   const int thePyraTo2_2[2*4+1] =
1690     {
1691       1, 2, 3, 4,    1, 3, 0, 4,   -1
1692     };
1693   const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1694
1695   const int thePentaTo3_1[3*4+1] =
1696     {
1697       0, 1, 2, 3,    1, 3, 4, 2,     2, 3, 4, 5,    -1
1698     };
1699   const int thePentaTo3_2[3*4+1] =
1700     {
1701       1, 2, 0, 4,    2, 4, 5, 0,     0, 4, 5, 3,    -1
1702     };
1703   const int thePentaTo3_3[3*4+1] =
1704     {
1705       2, 0, 1, 5,    0, 5, 3, 1,     1, 5, 3, 4,    -1
1706     };
1707   const int thePentaTo3_4[3*4+1] =
1708     {
1709       0, 1, 2, 3,    1, 3, 4, 5,     2, 3, 1, 5,    -1
1710     };
1711   const int thePentaTo3_5[3*4+1] =
1712     {
1713       1, 2, 0, 4,    2, 4, 5, 3,     0, 4, 2, 3,    -1
1714     };
1715   const int thePentaTo3_6[3*4+1] =
1716     {
1717       2, 0, 1, 5,    0, 5, 3, 4,     1, 5, 0, 4,    -1
1718     };
1719   const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1720                                 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1721
1722   // Methods of splitting hexahedron into prisms
1723
1724   const int theHexTo4Prisms_BT[6*4+1] = // bottom-top
1725     {
1726       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
1727     };
1728   const int theHexTo4Prisms_LR[6*4+1] = // left-right
1729     {
1730       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
1731     };
1732   const int theHexTo4Prisms_FB[6*4+1] = // front-back
1733     {
1734       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
1735     };
1736
1737   const int theHexTo2Prisms_BT_1[6*2+1] =
1738     {
1739       0, 1, 3, 4, 5, 7,    1, 2, 3, 5, 6, 7,   -1
1740     };
1741   const int theHexTo2Prisms_BT_2[6*2+1] =
1742     {
1743       0, 1, 2, 4, 5, 6,    0, 2, 3, 4, 6, 7,   -1
1744     };
1745   const int* theHexTo2Prisms_BT[2] = { theHexTo2Prisms_BT_1, theHexTo2Prisms_BT_2 };
1746
1747   const int theHexTo2Prisms_LR_1[6*2+1] =
1748     {
1749       1, 0, 4, 2, 3, 7,    1, 4, 5, 2, 7, 6,   -1
1750     };
1751   const int theHexTo2Prisms_LR_2[6*2+1] =
1752     {
1753       1, 0, 4, 2, 3, 7,    1, 4, 5, 2, 7, 6,   -1
1754     };
1755   const int* theHexTo2Prisms_LR[2] = { theHexTo2Prisms_LR_1, theHexTo2Prisms_LR_2 };
1756
1757   const int theHexTo2Prisms_FB_1[6*2+1] =
1758     {
1759       0, 3, 4, 1, 2, 5,    3, 7, 4, 2, 6, 5,   -1
1760     };
1761   const int theHexTo2Prisms_FB_2[6*2+1] =
1762     {
1763       0, 3, 7, 1, 2, 7,    0, 7, 4, 1, 6, 5,   -1
1764     };
1765   const int* theHexTo2Prisms_FB[2] = { theHexTo2Prisms_FB_1, theHexTo2Prisms_FB_2 };
1766
1767
1768   struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1769   {
1770     int _n1, _n2, _n3;
1771     TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1772     bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1773     bool hasAdjacentVol( const SMDS_MeshElement*    elem,
1774                          const SMDSAbs_GeometryType geom = SMDSGeom_TETRA) const;
1775   };
1776   struct TSplitMethod
1777   {
1778     int        _nbSplits;
1779     int        _nbCorners;
1780     const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1781     bool       _baryNode;     //!< additional node is to be created at cell barycenter
1782     bool       _ownConn;      //!< to delete _connectivity in destructor
1783     map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1784
1785     TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1786       : _nbSplits(nbTet), _nbCorners(4), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1787     ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1788     bool hasFacet( const TTriangleFacet& facet ) const
1789     {
1790       if ( _nbCorners == 4 )
1791       {
1792         const int* tetConn = _connectivity;
1793         for ( ; tetConn[0] >= 0; tetConn += 4 )
1794           if (( facet.contains( tetConn[0] ) +
1795                 facet.contains( tetConn[1] ) +
1796                 facet.contains( tetConn[2] ) +
1797                 facet.contains( tetConn[3] )) == 3 )
1798             return true;
1799       }
1800       else // prism, _nbCorners == 6
1801       {
1802         const int* prismConn = _connectivity;
1803         for ( ; prismConn[0] >= 0; prismConn += 6 )
1804         {
1805           if (( facet.contains( prismConn[0] ) &&
1806                 facet.contains( prismConn[1] ) &&
1807                 facet.contains( prismConn[2] ))
1808               ||
1809               ( facet.contains( prismConn[3] ) &&
1810                 facet.contains( prismConn[4] ) &&
1811                 facet.contains( prismConn[5] )))
1812             return true;
1813         }
1814       }
1815       return false;
1816     }
1817   };
1818
1819   //=======================================================================
1820   /*!
1821    * \brief return TSplitMethod for the given element to split into tetrahedra
1822    */
1823   //=======================================================================
1824
1825   TSplitMethod getTetraSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1826   {
1827     const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1828
1829     // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1830     // an edge and a face barycenter; tertaherdons are based on triangles and
1831     // a volume barycenter
1832     const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1833
1834     // Find out how adjacent volumes are split
1835
1836     vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1837     int hasAdjacentSplits = 0, maxTetConnSize = 0;
1838     for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1839     {
1840       int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1841       maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1842       if ( nbNodes < 4 ) continue;
1843
1844       list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1845       const int* nInd = vol.GetFaceNodesIndices( iF );
1846       if ( nbNodes == 4 )
1847       {
1848         TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1849         TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1850         if      ( t012.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t012 );
1851         else if ( t123.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t123 );
1852       }
1853       else
1854       {
1855         int iCom = 0; // common node of triangle faces to split into
1856         for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1857         {
1858           TTriangleFacet t012( nInd[ iQ * ( iCom             )],
1859                                nInd[ iQ * ( (iCom+1)%nbNodes )],
1860                                nInd[ iQ * ( (iCom+2)%nbNodes )]);
1861           TTriangleFacet t023( nInd[ iQ * ( iCom             )],
1862                                nInd[ iQ * ( (iCom+2)%nbNodes )],
1863                                nInd[ iQ * ( (iCom+3)%nbNodes )]);
1864           if ( t012.hasAdjacentVol( vol.Element() ) && t023.hasAdjacentVol( vol.Element() ))
1865           {
1866             triaSplits.push_back( t012 );
1867             triaSplits.push_back( t023 );
1868             break;
1869           }
1870         }
1871       }
1872       if ( !triaSplits.empty() )
1873         hasAdjacentSplits = true;
1874     }
1875
1876     // Among variants of split method select one compliant with adjacent volumes
1877
1878     TSplitMethod method;
1879     if ( !vol.Element()->IsPoly() && !is24TetMode )
1880     {
1881       int nbVariants = 2, nbTet = 0;
1882       const int** connVariants = 0;
1883       switch ( vol.Element()->GetEntityType() )
1884       {
1885       case SMDSEntity_Hexa:
1886       case SMDSEntity_Quad_Hexa:
1887       case SMDSEntity_TriQuad_Hexa:
1888         if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1889           connVariants = theHexTo5, nbTet = 5;
1890         else
1891           connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1892         break;
1893       case SMDSEntity_Pyramid:
1894       case SMDSEntity_Quad_Pyramid:
1895         connVariants = thePyraTo2;  nbTet = 2;
1896         break;
1897       case SMDSEntity_Penta:
1898       case SMDSEntity_Quad_Penta:
1899       case SMDSEntity_BiQuad_Penta:
1900         connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1901         break;
1902       default:
1903         nbVariants = 0;
1904       }
1905       for ( int variant = 0; variant < nbVariants && method._nbSplits == 0; ++variant )
1906       {
1907         // check method compliancy with adjacent tetras,
1908         // all found splits must be among facets of tetras described by this method
1909         method = TSplitMethod( nbTet, connVariants[variant] );
1910         if ( hasAdjacentSplits && method._nbSplits > 0 )
1911         {
1912           bool facetCreated = true;
1913           for ( size_t iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1914           {
1915             list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1916             for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1917               facetCreated = method.hasFacet( *facet );
1918           }
1919           if ( !facetCreated )
1920             method = TSplitMethod(0); // incompatible method
1921         }
1922       }
1923     }
1924     if ( method._nbSplits < 1 )
1925     {
1926       // No standard method is applicable, use a generic solution:
1927       // each facet of a volume is split into triangles and
1928       // each of triangles and a volume barycenter form a tetrahedron.
1929
1930       const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1931
1932       int* connectivity = new int[ maxTetConnSize + 1 ];
1933       method._connectivity = connectivity;
1934       method._ownConn = true;
1935       method._baryNode = !isHex27; // to create central node or not
1936
1937       int connSize = 0;
1938       int baryCenInd = vol.NbNodes() - int( isHex27 );
1939       for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1940       {
1941         const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1942         const int*   nInd = vol.GetFaceNodesIndices( iF );
1943         // find common node of triangle facets of tetra to create
1944         int iCommon = 0; // index in linear numeration
1945         const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1946         if ( !triaSplits.empty() )
1947         {
1948           // by found facets
1949           const TTriangleFacet* facet = &triaSplits.front();
1950           for ( ; iCommon < nbNodes-1 ; ++iCommon )
1951             if ( facet->contains( nInd[ iQ * iCommon ]) &&
1952                  facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1953               break;
1954         }
1955         else if ( nbNodes > 3 && !is24TetMode )
1956         {
1957           // find the best method of splitting into triangles by aspect ratio
1958           SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1959           map< double, int > badness2iCommon;
1960           const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1961           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1962           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1963           {
1964             double badness = 0;
1965             for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1966             {
1967               SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon         )],
1968                                       nodes[ iQ*((iLast-1)%nbNodes)],
1969                                       nodes[ iQ*((iLast  )%nbNodes)]);
1970               badness += getBadRate( &tria, aspectRatio );
1971             }
1972             badness2iCommon.insert( make_pair( badness, iCommon ));
1973           }
1974           // use iCommon with lowest badness
1975           iCommon = badness2iCommon.begin()->second;
1976         }
1977         if ( iCommon >= nbNodes )
1978           iCommon = 0; // something wrong
1979
1980         // fill connectivity of tetrahedra based on a current face
1981         int nbTet = nbNodes - 2;
1982         if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
1983         {
1984           int faceBaryCenInd;
1985           if ( isHex27 )
1986           {
1987             faceBaryCenInd = vol.GetCenterNodeIndex( iF );
1988             method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
1989           }
1990           else
1991           {
1992             method._faceBaryNode[ iF ] = 0;
1993             faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
1994           }
1995           nbTet = nbNodes;
1996           for ( int i = 0; i < nbTet; ++i )
1997           {
1998             int i1 = i, i2 = (i+1) % nbNodes;
1999             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2000             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2001             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2002             connectivity[ connSize++ ] = faceBaryCenInd;
2003             connectivity[ connSize++ ] = baryCenInd;
2004           }
2005         }
2006         else
2007         {
2008           for ( int i = 0; i < nbTet; ++i )
2009           {
2010             int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
2011             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2012             connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
2013             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2014             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2015             connectivity[ connSize++ ] = baryCenInd;
2016           }
2017         }
2018         method._nbSplits += nbTet;
2019
2020       } // loop on volume faces
2021
2022       connectivity[ connSize++ ] = -1;
2023
2024     } // end of generic solution
2025
2026     return method;
2027   }
2028   //=======================================================================
2029   /*!
2030    * \brief return TSplitMethod to split haxhedron into prisms
2031    */
2032   //=======================================================================
2033
2034   TSplitMethod getPrismSplitMethod( SMDS_VolumeTool& vol,
2035                                     const int        methodFlags,
2036                                     const int        facetToSplit)
2037   {
2038     // order of facets in HEX according to SMDS_VolumeTool::Hexa_F :
2039     // B, T, L, B, R, F
2040     const int iF = ( facetToSplit < 2 ) ? 0 : 1 + ( facetToSplit-2 ) % 2; // [0,1,2]
2041
2042     if ( methodFlags == SMESH_MeshEditor::HEXA_TO_4_PRISMS )
2043     {
2044       static TSplitMethod to4methods[4]; // order BT, LR, FB
2045       if ( to4methods[iF]._nbSplits == 0 )
2046       {
2047         switch ( iF ) {
2048         case 0:
2049           to4methods[iF]._connectivity = theHexTo4Prisms_BT;
2050           to4methods[iF]._faceBaryNode[ 0 ] = 0;
2051           to4methods[iF]._faceBaryNode[ 1 ] = 0;
2052           break;
2053         case 1:
2054           to4methods[iF]._connectivity = theHexTo4Prisms_LR;
2055           to4methods[iF]._faceBaryNode[ 2 ] = 0;
2056           to4methods[iF]._faceBaryNode[ 4 ] = 0;
2057           break;
2058         case 2:
2059           to4methods[iF]._connectivity = theHexTo4Prisms_FB;
2060           to4methods[iF]._faceBaryNode[ 3 ] = 0;
2061           to4methods[iF]._faceBaryNode[ 5 ] = 0;
2062           break;
2063         default: return to4methods[3];
2064         }
2065         to4methods[iF]._nbSplits  = 4;
2066         to4methods[iF]._nbCorners = 6;
2067       }
2068       return to4methods[iF];
2069     }
2070     // else if ( methodFlags == HEXA_TO_2_PRISMS )
2071
2072     TSplitMethod method;
2073
2074     const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2075
2076     const int nbVariants = 2, nbSplits = 2;
2077     const int** connVariants = 0;
2078     switch ( iF ) {
2079     case 0: connVariants = theHexTo2Prisms_BT; break;
2080     case 1: connVariants = theHexTo2Prisms_LR; break;
2081     case 2: connVariants = theHexTo2Prisms_FB; break;
2082     default: return method;
2083     }
2084
2085     // look for prisms adjacent via facetToSplit and an opposite one
2086     for ( int is2nd = 0; is2nd < 2; ++is2nd )
2087     {
2088       int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2089       int nbNodes = vol.NbFaceNodes( iFacet ) / iQ;
2090       if ( nbNodes != 4 ) return method;
2091
2092       const int* nInd = vol.GetFaceNodesIndices( iFacet );
2093       TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2094       TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2095       TTriangleFacet* t;
2096       if      ( t012.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2097         t = &t012;
2098       else if ( t123.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2099         t = &t123;
2100       else
2101         continue;
2102
2103       // there are adjacent prism
2104       for ( int variant = 0; variant < nbVariants; ++variant )
2105       {
2106         // check method compliancy with adjacent prisms,
2107         // the found prism facets must be among facets of prisms described by current method
2108         method._nbSplits     = nbSplits;
2109         method._nbCorners    = 6;
2110         method._connectivity = connVariants[ variant ];
2111         if ( method.hasFacet( *t ))
2112           return method;
2113       }
2114     }
2115
2116     // No adjacent prisms. Select a variant with a best aspect ratio.
2117
2118     double badness[2] = { 0., 0. };
2119     static SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
2120     const SMDS_MeshNode** nodes = vol.GetNodes();
2121     for ( int variant = 0; variant < nbVariants; ++variant )
2122       for ( int is2nd = 0; is2nd < 2; ++is2nd )
2123       {
2124         int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2125         const int*             nInd = vol.GetFaceNodesIndices( iFacet );
2126
2127         method._connectivity = connVariants[ variant ];
2128         TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2129         TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2130         TTriangleFacet* t = ( method.hasFacet( t012 )) ? & t012 : & t123;
2131
2132         SMDS_FaceOfNodes tria ( nodes[ t->_n1 ],
2133                                 nodes[ t->_n2 ],
2134                                 nodes[ t->_n3 ] );
2135         badness[ variant ] += getBadRate( &tria, aspectRatio );
2136       }
2137     const int iBetter = ( badness[1] < badness[0] && badness[0]-badness[1] > 0.1 * badness[0] );
2138
2139     method._nbSplits     = nbSplits;
2140     method._nbCorners    = 6;
2141     method._connectivity = connVariants[ iBetter ];
2142
2143     return method;
2144   }
2145
2146   //================================================================================
2147   /*!
2148    * \brief Check if there is a tetraherdon adjacent to the given element via this facet
2149    */
2150   //================================================================================
2151
2152   bool TTriangleFacet::hasAdjacentVol( const SMDS_MeshElement*    elem,
2153                                        const SMDSAbs_GeometryType geom ) const
2154   {
2155     // find the tetrahedron including the three nodes of facet
2156     const SMDS_MeshNode* n1 = elem->GetNode(_n1);
2157     const SMDS_MeshNode* n2 = elem->GetNode(_n2);
2158     const SMDS_MeshNode* n3 = elem->GetNode(_n3);
2159     SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
2160     while ( volIt1->more() )
2161     {
2162       const SMDS_MeshElement* v = volIt1->next();
2163       if ( v->GetGeomType() != geom )
2164         continue;
2165       const int lastCornerInd = v->NbCornerNodes() - 1;
2166       if ( v->IsQuadratic() && v->GetNodeIndex( n1 ) > lastCornerInd )
2167         continue; // medium node not allowed
2168       const int ind2 = v->GetNodeIndex( n2 );
2169       if ( ind2 < 0 || lastCornerInd < ind2 )
2170         continue;
2171       const int ind3 = v->GetNodeIndex( n3 );
2172       if ( ind3 < 0 || lastCornerInd < ind3 )
2173         continue;
2174       return true;
2175     }
2176     return false;
2177   }
2178
2179   //=======================================================================
2180   /*!
2181    * \brief A key of a face of volume
2182    */
2183   //=======================================================================
2184
2185   struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
2186   {
2187     TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
2188     {
2189       TIDSortedNodeSet sortedNodes;
2190       const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2191       int nbNodes = vol.NbFaceNodes( iF );
2192       const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
2193       for ( int i = 0; i < nbNodes; i += iQ )
2194         sortedNodes.insert( fNodes[i] );
2195       TIDSortedNodeSet::iterator n = sortedNodes.begin();
2196       first.first   = (*(n++))->GetID();
2197       first.second  = (*(n++))->GetID();
2198       second.first  = (*(n++))->GetID();
2199       second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
2200     }
2201   };
2202 } // namespace
2203
2204 //=======================================================================
2205 //function : SplitVolumes
2206 //purpose  : Split volume elements into tetrahedra or prisms.
2207 //           If facet ID < 0, element is split into tetrahedra,
2208 //           else a hexahedron is split into prisms so that the given facet is
2209 //           split into triangles
2210 //=======================================================================
2211
2212 void SMESH_MeshEditor::SplitVolumes (const TFacetOfElem & theElems,
2213                                      const int            theMethodFlags)
2214 {
2215   SMDS_VolumeTool    volTool;
2216   SMESH_MesherHelper helper( *GetMesh()), fHelper(*GetMesh());
2217   fHelper.ToFixNodeParameters( true );
2218
2219   SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
2220   SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
2221
2222   SMESH_SequenceOfElemPtr newNodes, newElems;
2223
2224   // map face of volume to it's baricenrtic node
2225   map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
2226   double bc[3];
2227   vector<const SMDS_MeshElement* > splitVols;
2228
2229   TFacetOfElem::const_iterator elem2facet = theElems.begin();
2230   for ( ; elem2facet != theElems.end(); ++elem2facet )
2231   {
2232     const SMDS_MeshElement* elem = elem2facet->first;
2233     const int       facetToSplit = elem2facet->second;
2234     if ( elem->GetType() != SMDSAbs_Volume )
2235       continue;
2236     const SMDSAbs_EntityType geomType = elem->GetEntityType();
2237     if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
2238       continue;
2239
2240     if ( !volTool.Set( elem, /*ignoreCentralNodes=*/false )) continue; // strange...
2241
2242     TSplitMethod splitMethod = ( facetToSplit < 0  ?
2243                                  getTetraSplitMethod( volTool, theMethodFlags ) :
2244                                  getPrismSplitMethod( volTool, theMethodFlags, facetToSplit ));
2245     if ( splitMethod._nbSplits < 1 ) continue;
2246
2247     // find submesh to add new tetras to
2248     if ( !subMesh || !subMesh->Contains( elem ))
2249     {
2250       int shapeID = FindShape( elem );
2251       helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
2252       subMesh = GetMeshDS()->MeshElements( shapeID );
2253     }
2254     int iQ;
2255     if ( elem->IsQuadratic() )
2256     {
2257       iQ = 2;
2258       // add quadratic links to the helper
2259       for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2260       {
2261         const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
2262         int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
2263         for ( int iN = 0; iN < nbN; iN += iQ )
2264           helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
2265       }
2266       helper.SetIsQuadratic( true );
2267     }
2268     else
2269     {
2270       iQ = 1;
2271       helper.SetIsQuadratic( false );
2272     }
2273     vector<const SMDS_MeshNode*> nodes( volTool.GetNodes(),
2274                                         volTool.GetNodes() + elem->NbNodes() );
2275     helper.SetElementsOnShape( true );
2276     if ( splitMethod._baryNode )
2277     {
2278       // make a node at barycenter
2279       volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
2280       SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
2281       nodes.push_back( gcNode );
2282       newNodes.push_back( gcNode );
2283     }
2284     if ( !splitMethod._faceBaryNode.empty() )
2285     {
2286       // make or find baricentric nodes of faces
2287       map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
2288       for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
2289       {
2290         map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
2291           volFace2BaryNode.insert
2292           ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
2293         if ( !f_n->second )
2294         {
2295           volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
2296           newNodes.push_back( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
2297         }
2298         nodes.push_back( iF_n->second = f_n->second );
2299       }
2300     }
2301
2302     // make new volumes
2303     splitVols.resize( splitMethod._nbSplits ); // splits of a volume
2304     const int* volConn = splitMethod._connectivity;
2305     if ( splitMethod._nbCorners == 4 ) // tetra
2306       for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2307         newElems.push_back( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2308                                                                nodes[ volConn[1] ],
2309                                                                nodes[ volConn[2] ],
2310                                                                nodes[ volConn[3] ]));
2311     else // prisms
2312       for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2313         newElems.push_back( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2314                                                                nodes[ volConn[1] ],
2315                                                                nodes[ volConn[2] ],
2316                                                                nodes[ volConn[3] ],
2317                                                                nodes[ volConn[4] ],
2318                                                                nodes[ volConn[5] ]));
2319
2320     ReplaceElemInGroups( elem, splitVols, GetMeshDS() );
2321
2322     // Split faces on sides of the split volume
2323
2324     const SMDS_MeshNode** volNodes = volTool.GetNodes();
2325     for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2326     {
2327       const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
2328       if ( nbNodes < 4 ) continue;
2329
2330       // find an existing face
2331       vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
2332                                            volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
2333       while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
2334                                                                        /*noMedium=*/false))
2335       {
2336         // make triangles
2337         helper.SetElementsOnShape( false );
2338         vector< const SMDS_MeshElement* > triangles;
2339
2340         // find submesh to add new triangles in
2341         if ( !fSubMesh || !fSubMesh->Contains( face ))
2342         {
2343           int shapeID = FindShape( face );
2344           fSubMesh = GetMeshDS()->MeshElements( shapeID );
2345         }
2346         map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
2347         if ( iF_n != splitMethod._faceBaryNode.end() )
2348         {
2349           const SMDS_MeshNode *baryNode = iF_n->second;
2350           for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
2351           {
2352             const SMDS_MeshNode* n1 = fNodes[iN];
2353             const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
2354             const SMDS_MeshNode *n3 = baryNode;
2355             if ( !volTool.IsFaceExternal( iF ))
2356               swap( n2, n3 );
2357             triangles.push_back( helper.AddFace( n1,n2,n3 ));
2358           }
2359           if ( fSubMesh ) // update position of the bary node on geometry
2360           {
2361             if ( subMesh )
2362               subMesh->RemoveNode( baryNode );
2363             GetMeshDS()->SetNodeOnFace( baryNode, fSubMesh->GetID() );
2364             const TopoDS_Shape& s = GetMeshDS()->IndexToShape( fSubMesh->GetID() );
2365             if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE )
2366             {
2367               fHelper.SetSubShape( s );
2368               gp_XY uv( 1e100, 1e100 );
2369               double distXYZ[4];
2370               if ( !fHelper.CheckNodeUV( TopoDS::Face( s ), baryNode,
2371                                          uv, /*tol=*/1e-7, /*force=*/true, distXYZ ) &&
2372                    uv.X() < 1e100 )
2373               {
2374                 // node is too far from the surface
2375                 GetMeshDS()->MoveNode( baryNode, distXYZ[1], distXYZ[2], distXYZ[3] );
2376                 const_cast<SMDS_MeshNode*>( baryNode )->SetPosition
2377                   ( SMDS_PositionPtr( new SMDS_FacePosition( uv.X(), uv.Y() )));
2378               }
2379             }
2380           }
2381         }
2382         else
2383         {
2384           // among possible triangles create ones described by split method
2385           const int* nInd = volTool.GetFaceNodesIndices( iF );
2386           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2387           int iCom = 0; // common node of triangle faces to split into
2388           list< TTriangleFacet > facets;
2389           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
2390           {
2391             TTriangleFacet t012( nInd[ iQ * ( iCom                )],
2392                                  nInd[ iQ * ( (iCom+1)%nbNodes )],
2393                                  nInd[ iQ * ( (iCom+2)%nbNodes )]);
2394             TTriangleFacet t023( nInd[ iQ * ( iCom                )],
2395                                  nInd[ iQ * ( (iCom+2)%nbNodes )],
2396                                  nInd[ iQ * ( (iCom+3)%nbNodes )]);
2397             if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
2398             {
2399               facets.push_back( t012 );
2400               facets.push_back( t023 );
2401               for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
2402                 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom             )],
2403                                                   nInd[ iQ * ((iLast-1)%nbNodes )],
2404                                                   nInd[ iQ * ((iLast  )%nbNodes )]));
2405               break;
2406             }
2407           }
2408           list< TTriangleFacet >::iterator facet = facets.begin();
2409           if ( facet == facets.end() )
2410             break;
2411           for ( ; facet != facets.end(); ++facet )
2412           {
2413             if ( !volTool.IsFaceExternal( iF ))
2414               swap( facet->_n2, facet->_n3 );
2415             triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
2416                                                  volNodes[ facet->_n2 ],
2417                                                  volNodes[ facet->_n3 ]));
2418           }
2419         }
2420         for ( size_t i = 0; i < triangles.size(); ++i )
2421         {
2422           if ( !triangles[ i ]) continue;
2423           if ( fSubMesh )
2424             fSubMesh->AddElement( triangles[ i ]);
2425           newElems.push_back( triangles[ i ]);
2426         }
2427         ReplaceElemInGroups( face, triangles, GetMeshDS() );
2428         GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2429
2430       } // while a face based on facet nodes exists
2431     } // loop on volume faces to split them into triangles
2432
2433     GetMeshDS()->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2434
2435     if ( geomType == SMDSEntity_TriQuad_Hexa )
2436     {
2437       // remove medium nodes that could become free
2438       for ( int i = 20; i < volTool.NbNodes(); ++i )
2439         if ( volNodes[i]->NbInverseElements() == 0 )
2440           GetMeshDS()->RemoveNode( volNodes[i] );
2441     }
2442   } // loop on volumes to split
2443
2444   myLastCreatedNodes = newNodes;
2445   myLastCreatedElems = newElems;
2446 }
2447
2448 //=======================================================================
2449 //function : GetHexaFacetsToSplit
2450 //purpose  : For hexahedra that will be split into prisms, finds facets to
2451 //           split into triangles. Only hexahedra adjacent to the one closest
2452 //           to theFacetNormal.Location() are returned.
2453 //param [in,out] theHexas - the hexahedra
2454 //param [in]     theFacetNormal - facet normal
2455 //param [out]    theFacets - the hexahedra and found facet IDs
2456 //=======================================================================
2457
2458 void SMESH_MeshEditor::GetHexaFacetsToSplit( TIDSortedElemSet& theHexas,
2459                                              const gp_Ax1&     theFacetNormal,
2460                                              TFacetOfElem &    theFacets)
2461 {
2462 #define THIS_METHOD "SMESH_MeshEditor::GetHexaFacetsToSplit(): "
2463
2464   // Find a hexa closest to the location of theFacetNormal
2465
2466   const SMDS_MeshElement* startHex;
2467   {
2468     // get SMDS_ElemIteratorPtr on theHexas
2469     typedef const SMDS_MeshElement*                                      TValue;
2470     typedef TIDSortedElemSet::iterator                                   TSetIterator;
2471     typedef SMDS::SimpleAccessor<TValue,TSetIterator>                    TAccesor;
2472     typedef SMDS_MeshElement::GeomFilter                                 TFilter;
2473     typedef SMDS_SetIterator < TValue, TSetIterator, TAccesor, TFilter > TElemSetIter;
2474     SMDS_ElemIteratorPtr elemIt = SMDS_ElemIteratorPtr
2475       ( new TElemSetIter( theHexas.begin(),
2476                           theHexas.end(),
2477                           SMDS_MeshElement::GeomFilter( SMDSGeom_HEXA )));
2478
2479     SMESH_ElementSearcher* searcher =
2480       SMESH_MeshAlgos::GetElementSearcher( *myMesh->GetMeshDS(), elemIt );
2481
2482     startHex = searcher->FindClosestTo( theFacetNormal.Location(), SMDSAbs_Volume );
2483
2484     delete searcher;
2485
2486     if ( !startHex )
2487       throw SALOME_Exception( THIS_METHOD "startHex not found");
2488   }
2489
2490   // Select a facet of startHex by theFacetNormal
2491
2492   SMDS_VolumeTool vTool( startHex );
2493   double norm[3], dot, maxDot = 0;
2494   int facetID = -1;
2495   for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2496     if ( vTool.GetFaceNormal( iF, norm[0], norm[1], norm[2] ))
2497     {
2498       dot = Abs( theFacetNormal.Direction().Dot( gp_Dir( norm[0], norm[1], norm[2] )));
2499       if ( dot > maxDot )
2500       {
2501         facetID = iF;
2502         maxDot = dot;
2503       }
2504     }
2505   if ( facetID < 0 )
2506     throw SALOME_Exception( THIS_METHOD "facet of startHex not found");
2507
2508   // Fill theFacets starting from facetID of startHex
2509
2510   // facets used for searching of volumes adjacent to already treated ones
2511   typedef pair< TFacetOfElem::iterator, int > TElemFacets;
2512   typedef map< TVolumeFaceKey, TElemFacets  > TFacetMap;
2513   TFacetMap facetsToCheck;
2514
2515   set<const SMDS_MeshNode*> facetNodes;
2516   const SMDS_MeshElement*   curHex;
2517
2518   const bool allHex = ((int) theHexas.size() == myMesh->NbHexas() );
2519
2520   while ( startHex )
2521   {
2522     // move in two directions from startHex via facetID
2523     for ( int is2nd = 0; is2nd < 2; ++is2nd )
2524     {
2525       curHex       = startHex;
2526       int curFacet = facetID;
2527       if ( is2nd ) // do not treat startHex twice
2528       {
2529         vTool.Set( curHex );
2530         if ( vTool.IsFreeFace( curFacet, &curHex ))
2531         {
2532           curHex = 0;
2533         }
2534         else
2535         {
2536           vTool.GetFaceNodes( curFacet, facetNodes );
2537           vTool.Set( curHex );
2538           curFacet = vTool.GetFaceIndex( facetNodes );
2539         }
2540       }
2541       while ( curHex )
2542       {
2543         // store a facet to split
2544         if ( curHex->GetGeomType() != SMDSGeom_HEXA )
2545         {
2546           theFacets.insert( make_pair( curHex, -1 ));
2547           break;
2548         }
2549         if ( !allHex && !theHexas.count( curHex ))
2550           break;
2551
2552         pair< TFacetOfElem::iterator, bool > facetIt2isNew =
2553           theFacets.insert( make_pair( curHex, curFacet ));
2554         if ( !facetIt2isNew.second )
2555           break;
2556
2557         // remember not-to-split facets in facetsToCheck
2558         int oppFacet = vTool.GetOppFaceIndexOfHex( curFacet );
2559         for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2560         {
2561           if ( iF == curFacet && iF == oppFacet )
2562             continue;
2563           TVolumeFaceKey facetKey ( vTool, iF );
2564           TElemFacets    elemFacet( facetIt2isNew.first, iF );
2565           pair< TFacetMap::iterator, bool > it2isnew =
2566             facetsToCheck.insert( make_pair( facetKey, elemFacet ));
2567           if ( !it2isnew.second )
2568             facetsToCheck.erase( it2isnew.first ); // adjacent hex already checked
2569         }
2570         // pass to a volume adjacent via oppFacet
2571         if ( vTool.IsFreeFace( oppFacet, &curHex ))
2572         {
2573           curHex = 0;
2574         }
2575         else
2576         {
2577           // get a new curFacet
2578           vTool.GetFaceNodes( oppFacet, facetNodes );
2579           vTool.Set( curHex );
2580           curFacet = vTool.GetFaceIndex( facetNodes, /*hint=*/curFacet );
2581         }
2582       }
2583     } // move in two directions from startHex via facetID
2584
2585     // Find a new startHex by facetsToCheck
2586
2587     startHex = 0;
2588     facetID  = -1;
2589     TFacetMap::iterator fIt = facetsToCheck.begin();
2590     while ( !startHex && fIt != facetsToCheck.end() )
2591     {
2592       const TElemFacets&  elemFacets = fIt->second;
2593       const SMDS_MeshElement*    hex = elemFacets.first->first;
2594       int                 splitFacet = elemFacets.first->second;
2595       int               lateralFacet = elemFacets.second;
2596       facetsToCheck.erase( fIt );
2597       fIt = facetsToCheck.begin();
2598
2599       vTool.Set( hex );
2600       if ( vTool.IsFreeFace( lateralFacet, &curHex ) || 
2601            curHex->GetGeomType() != SMDSGeom_HEXA )
2602         continue;
2603       if ( !allHex && !theHexas.count( curHex ))
2604         continue;
2605
2606       startHex = curHex;
2607
2608       // find a facet of startHex to split
2609
2610       set<const SMDS_MeshNode*> lateralNodes;
2611       vTool.GetFaceNodes( lateralFacet, lateralNodes );
2612       vTool.GetFaceNodes( splitFacet,   facetNodes );
2613       int oppLateralFacet = vTool.GetOppFaceIndexOfHex( lateralFacet );
2614       vTool.Set( startHex );
2615       lateralFacet = vTool.GetFaceIndex( lateralNodes, oppLateralFacet );
2616
2617       // look for a facet of startHex having common nodes with facetNodes
2618       // but not lateralFacet
2619       for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2620       {
2621         if ( iF == lateralFacet )
2622           continue;
2623         int nbCommonNodes = 0;
2624         const SMDS_MeshNode** nn = vTool.GetFaceNodes( iF );
2625         for ( int iN = 0, nbN = vTool.NbFaceNodes( iF ); iN < nbN; ++iN )
2626           nbCommonNodes += facetNodes.count( nn[ iN ]);
2627
2628         if ( nbCommonNodes >= 2 )
2629         {
2630           facetID = iF;
2631           break;
2632         }
2633       }
2634       if ( facetID < 0 )
2635         throw SALOME_Exception( THIS_METHOD "facet of a new startHex not found");
2636     }
2637   } //   while ( startHex )
2638
2639   return;
2640 }
2641
2642 namespace
2643 {
2644   //================================================================================
2645   /*!
2646    * \brief Selects nodes of several elements according to a given interlace
2647    *  \param [in] srcNodes - nodes to select from
2648    *  \param [out] tgtNodesVec - array of nodes of several elements to fill in
2649    *  \param [in] interlace - indices of nodes for all elements
2650    *  \param [in] nbElems - nb of elements
2651    *  \param [in] nbNodes - nb of nodes in each element
2652    *  \param [in] mesh - the mesh
2653    *  \param [out] elemQueue - a list to push elements found by the selected nodes
2654    *  \param [in] type - type of elements to look for
2655    */
2656   //================================================================================
2657
2658   void selectNodes( const vector< const SMDS_MeshNode* >& srcNodes,
2659                     vector< const SMDS_MeshNode* >*       tgtNodesVec,
2660                     const int*                            interlace,
2661                     const int                             nbElems,
2662                     const int                             nbNodes,
2663                     SMESHDS_Mesh*                         mesh = 0,
2664                     list< const SMDS_MeshElement* >*      elemQueue=0,
2665                     SMDSAbs_ElementType                   type=SMDSAbs_All)
2666   {
2667     for ( int iE = 0; iE < nbElems; ++iE )
2668     {
2669       vector< const SMDS_MeshNode* >& elemNodes = tgtNodesVec[iE];
2670       const int*                         select = & interlace[iE*nbNodes];
2671       elemNodes.resize( nbNodes );
2672       for ( int iN = 0; iN < nbNodes; ++iN )
2673         elemNodes[iN] = srcNodes[ select[ iN ]];
2674     }
2675     const SMDS_MeshElement* e;
2676     if ( elemQueue )
2677       for ( int iE = 0; iE < nbElems; ++iE )
2678         if (( e = mesh->FindElement( tgtNodesVec[iE], type, /*noMedium=*/false)))
2679           elemQueue->push_back( e );
2680   }
2681 }
2682
2683 //=======================================================================
2684 /*
2685  * Split bi-quadratic elements into linear ones without creation of additional nodes
2686  *   - bi-quadratic triangle will be split into 3 linear quadrangles;
2687  *   - bi-quadratic quadrangle will be split into 4 linear quadrangles;
2688  *   - tri-quadratic hexahedron will be split into 8 linear hexahedra;
2689  *   Quadratic elements of lower dimension  adjacent to the split bi-quadratic element
2690  *   will be split in order to keep the mesh conformal.
2691  *  \param elems - elements to split
2692  */
2693 //=======================================================================
2694
2695 void SMESH_MeshEditor::SplitBiQuadraticIntoLinear(TIDSortedElemSet& theElems)
2696 {
2697   vector< const SMDS_MeshNode* > elemNodes(27), subNodes[12], splitNodes[8];
2698   vector<const SMDS_MeshElement* > splitElems;
2699   list< const SMDS_MeshElement* > elemQueue;
2700   list< const SMDS_MeshElement* >::iterator elemIt;
2701
2702   SMESHDS_Mesh * mesh = GetMeshDS();
2703   ElemFeatures *elemType, hexaType(SMDSAbs_Volume), quadType(SMDSAbs_Face), segType(SMDSAbs_Edge);
2704   int nbElems, nbNodes;
2705
2706   TIDSortedElemSet::iterator elemSetIt = theElems.begin();
2707   for ( ; elemSetIt != theElems.end(); ++elemSetIt )
2708   {
2709     elemQueue.clear();
2710     elemQueue.push_back( *elemSetIt );
2711     for ( elemIt = elemQueue.begin(); elemIt != elemQueue.end(); ++elemIt )
2712     {
2713       const SMDS_MeshElement* elem = *elemIt;
2714       switch( elem->GetEntityType() )
2715       {
2716       case SMDSEntity_TriQuad_Hexa: // HEX27
2717       {
2718         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2719         nbElems  = nbNodes = 8;
2720         elemType = & hexaType;
2721
2722         // get nodes for new elements
2723         static int vInd[8][8] = {{ 0,8,20,11,   16,21,26,24 },
2724                                  { 1,9,20,8,    17,22,26,21 },
2725                                  { 2,10,20,9,   18,23,26,22 },
2726                                  { 3,11,20,10,  19,24,26,23 },
2727                                  { 16,21,26,24, 4,12,25,15  },
2728                                  { 17,22,26,21, 5,13,25,12  },
2729                                  { 18,23,26,22, 6,14,25,13  },
2730                                  { 19,24,26,23, 7,15,25,14  }};
2731         selectNodes( elemNodes, & splitNodes[0], &vInd[0][0], nbElems, nbNodes );
2732
2733         // add boundary faces to elemQueue
2734         static int fInd[6][9] = {{ 0,1,2,3, 8,9,10,11,   20 },
2735                                  { 4,5,6,7, 12,13,14,15, 25 },
2736                                  { 0,1,5,4, 8,17,12,16,  21 },
2737                                  { 1,2,6,5, 9,18,13,17,  22 },
2738                                  { 2,3,7,6, 10,19,14,18, 23 },
2739                                  { 3,0,4,7, 11,16,15,19, 24 }};
2740         selectNodes( elemNodes, & subNodes[0], &fInd[0][0], 6,9, mesh, &elemQueue, SMDSAbs_Face );
2741
2742         // add boundary segments to elemQueue
2743         static int eInd[12][3] = {{ 0,1,8 }, { 1,2,9 }, { 2,3,10 }, { 3,0,11 },
2744                                   { 4,5,12}, { 5,6,13}, { 6,7,14 }, { 7,4,15 },
2745                                   { 0,4,16}, { 1,5,17}, { 2,6,18 }, { 3,7,19 }};
2746         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 12,3, mesh, &elemQueue, SMDSAbs_Edge );
2747         break;
2748       }
2749       case SMDSEntity_BiQuad_Triangle: // TRIA7
2750       {
2751         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2752         nbElems = 3;
2753         nbNodes = 4;
2754         elemType = & quadType;
2755
2756         // get nodes for new elements
2757         static int fInd[3][4] = {{ 0,3,6,5 }, { 1,4,6,3 }, { 2,5,6,4 }};
2758         selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2759
2760         // add boundary segments to elemQueue
2761         static int eInd[3][3] = {{ 0,1,3 }, { 1,2,4 }, { 2,0,5 }};
2762         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 3,3, mesh, &elemQueue, SMDSAbs_Edge );
2763         break;
2764       }
2765       case SMDSEntity_BiQuad_Quadrangle: // QUAD9
2766       {
2767         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2768         nbElems = 4;
2769         nbNodes = 4;
2770         elemType = & quadType;
2771
2772         // get nodes for new elements
2773         static int fInd[4][4] = {{ 0,4,8,7 }, { 1,5,8,4 }, { 2,6,8,5 }, { 3,7,8,6 }};
2774         selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2775
2776         // add boundary segments to elemQueue
2777         static int eInd[4][3] = {{ 0,1,4 }, { 1,2,5 }, { 2,3,6 }, { 3,0,7 }};
2778         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 4,3, mesh, &elemQueue, SMDSAbs_Edge );
2779         break;
2780       }
2781       case SMDSEntity_Quad_Edge:
2782       {
2783         if ( elemIt == elemQueue.begin() )
2784           continue; // an elem is in theElems
2785         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2786         nbElems = 2;
2787         nbNodes = 2;
2788         elemType = & segType;
2789
2790         // get nodes for new elements
2791         static int eInd[2][2] = {{ 0,2 }, { 2,1 }};
2792         selectNodes( elemNodes, & splitNodes[0], &eInd[0][0], nbElems, nbNodes );
2793         break;
2794       }
2795       default: continue;
2796       } // switch( elem->GetEntityType() )
2797
2798       // Create new elements
2799
2800       SMESHDS_SubMesh* subMesh = mesh->MeshElements( elem->getshapeId() );
2801
2802       splitElems.clear();
2803
2804       //elemType->SetID( elem->GetID() ); // create an elem with the same ID as a removed one
2805       mesh->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2806       //splitElems.push_back( AddElement( splitNodes[ 0 ], *elemType ));
2807       //elemType->SetID( -1 );
2808
2809       for ( int iE = 0; iE < nbElems; ++iE )
2810         splitElems.push_back( AddElement( splitNodes[ iE ], *elemType ));
2811
2812
2813       ReplaceElemInGroups( elem, splitElems, mesh );
2814
2815       if ( subMesh )
2816         for ( size_t i = 0; i < splitElems.size(); ++i )
2817           subMesh->AddElement( splitElems[i] );
2818     }
2819   }
2820 }
2821
2822 //=======================================================================
2823 //function : AddToSameGroups
2824 //purpose  : add elemToAdd to the groups the elemInGroups belongs to
2825 //=======================================================================
2826
2827 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
2828                                         const SMDS_MeshElement* elemInGroups,
2829                                         SMESHDS_Mesh *          aMesh)
2830 {
2831   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2832   if (!groups.empty()) {
2833     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2834     for ( ; grIt != groups.end(); grIt++ ) {
2835       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2836       if ( group && group->Contains( elemInGroups ))
2837         group->SMDSGroup().Add( elemToAdd );
2838     }
2839   }
2840 }
2841
2842
2843 //=======================================================================
2844 //function : RemoveElemFromGroups
2845 //purpose  : Remove removeelem to the groups the elemInGroups belongs to
2846 //=======================================================================
2847 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2848                                              SMESHDS_Mesh *          aMesh)
2849 {
2850   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2851   if (!groups.empty())
2852   {
2853     set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2854     for (; GrIt != groups.end(); GrIt++)
2855     {
2856       SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2857       if (!grp || grp->IsEmpty()) continue;
2858       grp->SMDSGroup().Remove(removeelem);
2859     }
2860   }
2861 }
2862
2863 //================================================================================
2864 /*!
2865  * \brief Replace elemToRm by elemToAdd in the all groups
2866  */
2867 //================================================================================
2868
2869 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2870                                             const SMDS_MeshElement* elemToAdd,
2871                                             SMESHDS_Mesh *          aMesh)
2872 {
2873   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2874   if (!groups.empty()) {
2875     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2876     for ( ; grIt != groups.end(); grIt++ ) {
2877       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2878       if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2879         group->SMDSGroup().Add( elemToAdd );
2880     }
2881   }
2882 }
2883
2884 //================================================================================
2885 /*!
2886  * \brief Replace elemToRm by elemToAdd in the all groups
2887  */
2888 //================================================================================
2889
2890 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement*                elemToRm,
2891                                             const vector<const SMDS_MeshElement*>& elemToAdd,
2892                                             SMESHDS_Mesh *                         aMesh)
2893 {
2894   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2895   if (!groups.empty())
2896   {
2897     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2898     for ( ; grIt != groups.end(); grIt++ ) {
2899       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2900       if ( group && group->SMDSGroup().Remove( elemToRm ) )
2901         for ( size_t i = 0; i < elemToAdd.size(); ++i )
2902           group->SMDSGroup().Add( elemToAdd[ i ] );
2903     }
2904   }
2905 }
2906
2907 //=======================================================================
2908 //function : QuadToTri
2909 //purpose  : Cut quadrangles into triangles.
2910 //           theCrit is used to select a diagonal to cut
2911 //=======================================================================
2912
2913 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2914                                   const bool         the13Diag)
2915 {
2916   ClearLastCreated();
2917   myLastCreatedElems.reserve( theElems.size() * 2 );
2918
2919   SMESHDS_Mesh *       aMesh = GetMeshDS();
2920   Handle(Geom_Surface) surface;
2921   SMESH_MesherHelper   helper( *GetMesh() );
2922
2923   TIDSortedElemSet::iterator itElem;
2924   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
2925   {
2926     const SMDS_MeshElement* elem = *itElem;
2927     if ( !elem || elem->GetGeomType() != SMDSGeom_QUADRANGLE )
2928       continue;
2929
2930     if ( elem->NbNodes() == 4 ) {
2931       // retrieve element nodes
2932       const SMDS_MeshNode* aNodes [4];
2933       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2934       int i = 0;
2935       while ( itN->more() )
2936         aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2937
2938       int aShapeId = FindShape( elem );
2939       const SMDS_MeshElement* newElem1 = 0;
2940       const SMDS_MeshElement* newElem2 = 0;
2941       if ( the13Diag ) {
2942         newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2943         newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2944       }
2945       else {
2946         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2947         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2948       }
2949       myLastCreatedElems.push_back(newElem1);
2950       myLastCreatedElems.push_back(newElem2);
2951       // put a new triangle on the same shape and add to the same groups
2952       if ( aShapeId )
2953       {
2954         aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2955         aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2956       }
2957       AddToSameGroups( newElem1, elem, aMesh );
2958       AddToSameGroups( newElem2, elem, aMesh );
2959       aMesh->RemoveElement( elem );
2960     }
2961
2962     // Quadratic quadrangle
2963
2964     else if ( elem->NbNodes() >= 8 )
2965     {
2966       // get surface elem is on
2967       int aShapeId = FindShape( elem );
2968       if ( aShapeId != helper.GetSubShapeID() ) {
2969         surface.Nullify();
2970         TopoDS_Shape shape;
2971         if ( aShapeId > 0 )
2972           shape = aMesh->IndexToShape( aShapeId );
2973         if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
2974           TopoDS_Face face = TopoDS::Face( shape );
2975           surface = BRep_Tool::Surface( face );
2976           if ( !surface.IsNull() )
2977             helper.SetSubShape( shape );
2978         }
2979       }
2980
2981       const SMDS_MeshNode* aNodes [9]; aNodes[8] = 0;
2982       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2983       for ( int i = 0; itN->more(); ++i )
2984         aNodes[ i ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2985
2986       const SMDS_MeshNode* centrNode = aNodes[8];
2987       if ( centrNode == 0 )
2988       {
2989         centrNode = helper.GetCentralNode( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
2990                                            aNodes[4], aNodes[5], aNodes[6], aNodes[7],
2991                                            surface.IsNull() );
2992         myLastCreatedNodes.push_back(centrNode);
2993       }
2994
2995       // create a new element
2996       const SMDS_MeshElement* newElem1 = 0;
2997       const SMDS_MeshElement* newElem2 = 0;
2998       if ( the13Diag ) {
2999         newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
3000                                   aNodes[6], aNodes[7], centrNode );
3001         newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
3002                                   centrNode, aNodes[4], aNodes[5] );
3003       }
3004       else {
3005         newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
3006                                   aNodes[7], aNodes[4], centrNode );
3007         newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
3008                                   centrNode, aNodes[5], aNodes[6] );
3009       }
3010       myLastCreatedElems.push_back(newElem1);
3011       myLastCreatedElems.push_back(newElem2);
3012       // put a new triangle on the same shape and add to the same groups
3013       if ( aShapeId )
3014       {
3015         aMesh->SetMeshElementOnShape( newElem1, aShapeId );
3016         aMesh->SetMeshElementOnShape( newElem2, aShapeId );
3017       }
3018       AddToSameGroups( newElem1, elem, aMesh );
3019       AddToSameGroups( newElem2, elem, aMesh );
3020       aMesh->RemoveElement( elem );
3021     }
3022   }
3023
3024   return true;
3025 }
3026
3027 //=======================================================================
3028 //function : getAngle
3029 //purpose  :
3030 //=======================================================================
3031
3032 double getAngle(const SMDS_MeshElement * tr1,
3033                 const SMDS_MeshElement * tr2,
3034                 const SMDS_MeshNode *    n1,
3035                 const SMDS_MeshNode *    n2)
3036 {
3037   double angle = 2. * M_PI; // bad angle
3038
3039   // get normals
3040   SMESH::Controls::TSequenceOfXYZ P1, P2;
3041   if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
3042        !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
3043     return angle;
3044   gp_Vec N1,N2;
3045   if(!tr1->IsQuadratic())
3046     N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
3047   else
3048     N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
3049   if ( N1.SquareMagnitude() <= gp::Resolution() )
3050     return angle;
3051   if(!tr2->IsQuadratic())
3052     N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
3053   else
3054     N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
3055   if ( N2.SquareMagnitude() <= gp::Resolution() )
3056     return angle;
3057
3058   // find the first diagonal node n1 in the triangles:
3059   // take in account a diagonal link orientation
3060   const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
3061   for ( int t = 0; t < 2; t++ ) {
3062     SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
3063     int i = 0, iDiag = -1;
3064     while ( it->more()) {
3065       const SMDS_MeshElement *n = it->next();
3066       if ( n == n1 || n == n2 ) {
3067         if ( iDiag < 0)
3068           iDiag = i;
3069         else {
3070           if ( i - iDiag == 1 )
3071             nFirst[ t ] = ( n == n1 ? n2 : n1 );
3072           else
3073             nFirst[ t ] = n;
3074           break;
3075         }
3076       }
3077       i++;
3078     }
3079   }
3080   if ( nFirst[ 0 ] == nFirst[ 1 ] )
3081     N2.Reverse();
3082
3083   angle = N1.Angle( N2 );
3084   //SCRUTE( angle );
3085   return angle;
3086 }
3087
3088 // =================================================
3089 // class generating a unique ID for a pair of nodes
3090 // and able to return nodes by that ID
3091 // =================================================
3092 class LinkID_Gen {
3093 public:
3094
3095   LinkID_Gen( const SMESHDS_Mesh* theMesh )
3096     :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
3097   {}
3098
3099   long GetLinkID (const SMDS_MeshNode * n1,
3100                   const SMDS_MeshNode * n2) const
3101   {
3102     return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
3103   }
3104
3105   bool GetNodes (const long             theLinkID,
3106                  const SMDS_MeshNode* & theNode1,
3107                  const SMDS_MeshNode* & theNode2) const
3108   {
3109     theNode1 = myMesh->FindNode( theLinkID / myMaxID );
3110     if ( !theNode1 ) return false;
3111     theNode2 = myMesh->FindNode( theLinkID % myMaxID );
3112     if ( !theNode2 ) return false;
3113     return true;
3114   }
3115
3116 private:
3117   LinkID_Gen();
3118   const SMESHDS_Mesh* myMesh;
3119   long                myMaxID;
3120 };
3121
3122
3123 //=======================================================================
3124 //function : TriToQuad
3125 //purpose  : Fuse neighbour triangles into quadrangles.
3126 //           theCrit is used to select a neighbour to fuse with.
3127 //           theMaxAngle is a max angle between element normals at which
3128 //           fusion is still performed.
3129 //=======================================================================
3130
3131 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet &                   theElems,
3132                                   SMESH::Controls::NumericalFunctorPtr theCrit,
3133                                   const double                         theMaxAngle)
3134 {
3135   ClearLastCreated();
3136   myLastCreatedElems.reserve( theElems.size() / 2 );
3137
3138   if ( !theCrit.get() )
3139     return false;
3140
3141   SMESHDS_Mesh * aMesh = GetMeshDS();
3142
3143   // Prepare data for algo: build
3144   // 1. map of elements with their linkIDs
3145   // 2. map of linkIDs with their elements
3146
3147   map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
3148   map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
3149   map< const SMDS_MeshElement*, set< SMESH_TLink > >  mapEl_setLi;
3150   map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
3151
3152   TIDSortedElemSet::iterator itElem;
3153   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
3154   {
3155     const SMDS_MeshElement* elem = *itElem;
3156     if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
3157     bool IsTria = ( elem->NbCornerNodes()==3 );
3158     if (!IsTria) continue;
3159
3160     // retrieve element nodes
3161     const SMDS_MeshNode* aNodes [4];
3162     SMDS_NodeIteratorPtr itN = elem->nodeIterator();
3163     int i = 0;
3164     while ( i < 3 )
3165       aNodes[ i++ ] = itN->next();
3166     aNodes[ 3 ] = aNodes[ 0 ];
3167
3168     // fill maps
3169     for ( i = 0; i < 3; i++ ) {
3170       SMESH_TLink link( aNodes[i], aNodes[i+1] );
3171       // check if elements sharing a link can be fused
3172       itLE = mapLi_listEl.find( link );
3173       if ( itLE != mapLi_listEl.end() ) {
3174         if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
3175           continue;
3176         const SMDS_MeshElement* elem2 = (*itLE).second.front();
3177         //if ( FindShape( elem ) != FindShape( elem2 ))
3178         //  continue; // do not fuse triangles laying on different shapes
3179         if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
3180           continue; // avoid making badly shaped quads
3181         (*itLE).second.push_back( elem );
3182       }
3183       else {
3184         mapLi_listEl[ link ].push_back( elem );
3185       }
3186       mapEl_setLi [ elem ].insert( link );
3187     }
3188   }
3189   // Clean the maps from the links shared by a sole element, ie
3190   // links to which only one element is bound in mapLi_listEl
3191
3192   for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
3193     int nbElems = (*itLE).second.size();
3194     if ( nbElems < 2  ) {
3195       const SMDS_MeshElement* elem = (*itLE).second.front();
3196       SMESH_TLink link = (*itLE).first;
3197       mapEl_setLi[ elem ].erase( link );
3198       if ( mapEl_setLi[ elem ].empty() )
3199         mapEl_setLi.erase( elem );
3200     }
3201   }
3202
3203   // Algo: fuse triangles into quadrangles
3204
3205   while ( ! mapEl_setLi.empty() ) {
3206     // Look for the start element:
3207     // the element having the least nb of shared links
3208     const SMDS_MeshElement* startElem = 0;
3209     int minNbLinks = 4;
3210     for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
3211       int nbLinks = (*itEL).second.size();
3212       if ( nbLinks < minNbLinks ) {
3213         startElem = (*itEL).first;
3214         minNbLinks = nbLinks;
3215         if ( minNbLinks == 1 )
3216           break;
3217       }
3218     }
3219
3220     // search elements to fuse starting from startElem or links of elements
3221     // fused earlyer - startLinks
3222     list< SMESH_TLink > startLinks;
3223     while ( startElem || !startLinks.empty() ) {
3224       while ( !startElem && !startLinks.empty() ) {
3225         // Get an element to start, by a link
3226         SMESH_TLink linkId = startLinks.front();
3227         startLinks.pop_front();
3228         itLE = mapLi_listEl.find( linkId );
3229         if ( itLE != mapLi_listEl.end() ) {
3230           list< const SMDS_MeshElement* > & listElem = (*itLE).second;
3231           list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
3232           for ( ; itE != listElem.end() ; itE++ )
3233             if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
3234               startElem = (*itE);
3235           mapLi_listEl.erase( itLE );
3236         }
3237       }
3238
3239       if ( startElem ) {
3240         // Get candidates to be fused
3241         const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
3242         const SMESH_TLink *link12 = 0, *link13 = 0;
3243         startElem = 0;
3244         ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
3245         set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
3246         ASSERT( !setLi.empty() );
3247         set< SMESH_TLink >::iterator itLi;
3248         for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
3249         {
3250           const SMESH_TLink & link = (*itLi);
3251           itLE = mapLi_listEl.find( link );
3252           if ( itLE == mapLi_listEl.end() )
3253             continue;
3254
3255           const SMDS_MeshElement* elem = (*itLE).second.front();
3256           if ( elem == tr1 )
3257             elem = (*itLE).second.back();
3258           mapLi_listEl.erase( itLE );
3259           if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
3260             continue;
3261           if ( tr2 ) {
3262             tr3 = elem;
3263             link13 = &link;
3264           }
3265           else {
3266             tr2 = elem;
3267             link12 = &link;
3268           }
3269
3270           // add other links of elem to list of links to re-start from
3271           set< SMESH_TLink >& links = mapEl_setLi[ elem ];
3272           set< SMESH_TLink >::iterator it;
3273           for ( it = links.begin(); it != links.end(); it++ ) {
3274             const SMESH_TLink& link2 = (*it);
3275             if ( link2 != link )
3276               startLinks.push_back( link2 );
3277           }
3278         }
3279
3280         // Get nodes of possible quadrangles
3281         const SMDS_MeshNode *n12 [4], *n13 [4];
3282         bool Ok12 = false, Ok13 = false;
3283         const SMDS_MeshNode *linkNode1, *linkNode2;
3284         if(tr2) {
3285           linkNode1 = link12->first;
3286           linkNode2 = link12->second;
3287           if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
3288             Ok12 = true;
3289         }
3290         if(tr3) {
3291           linkNode1 = link13->first;
3292           linkNode2 = link13->second;
3293           if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
3294             Ok13 = true;
3295         }
3296
3297         // Choose a pair to fuse
3298         if ( Ok12 && Ok13 ) {
3299           SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
3300           SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
3301           double aBadRate12 = getBadRate( &quad12, theCrit );
3302           double aBadRate13 = getBadRate( &quad13, theCrit );
3303           if (  aBadRate13 < aBadRate12 )
3304             Ok12 = false;
3305           else
3306             Ok13 = false;
3307         }
3308
3309         // Make quadrangles
3310         // and remove fused elems and remove links from the maps
3311         mapEl_setLi.erase( tr1 );
3312         if ( Ok12 )
3313         {
3314           mapEl_setLi.erase( tr2 );
3315           mapLi_listEl.erase( *link12 );
3316           if ( tr1->NbNodes() == 3 )
3317           {
3318             const SMDS_MeshElement* newElem = 0;
3319             newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
3320             myLastCreatedElems.push_back(newElem);
3321             AddToSameGroups( newElem, tr1, aMesh );
3322             int aShapeId = tr1->getshapeId();
3323             if ( aShapeId )
3324               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3325             aMesh->RemoveElement( tr1 );
3326             aMesh->RemoveElement( tr2 );
3327           }
3328           else {
3329             vector< const SMDS_MeshNode* > N1;
3330             vector< const SMDS_MeshNode* > N2;
3331             getNodesFromTwoTria(tr1,tr2,N1,N2);
3332             // now we receive following N1 and N2 (using numeration as in image in InverseDiag())
3333             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
3334             // i.e. first nodes from both arrays form a new diagonal
3335             const SMDS_MeshNode* aNodes[8];
3336             aNodes[0] = N1[0];
3337             aNodes[1] = N1[1];
3338             aNodes[2] = N2[0];
3339             aNodes[3] = N2[1];
3340             aNodes[4] = N1[3];
3341             aNodes[5] = N2[5];
3342             aNodes[6] = N2[3];
3343             aNodes[7] = N1[5];
3344             const SMDS_MeshElement* newElem = 0;
3345             if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3346               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3347                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3348             else
3349               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3350                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3351             myLastCreatedElems.push_back(newElem);
3352             AddToSameGroups( newElem, tr1, aMesh );
3353             int aShapeId = tr1->getshapeId();
3354             if ( aShapeId )
3355               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3356             aMesh->RemoveElement( tr1 );
3357             aMesh->RemoveElement( tr2 );
3358             // remove middle node (9)
3359             if ( N1[4]->NbInverseElements() == 0 )
3360               aMesh->RemoveNode( N1[4] );
3361             if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3362               aMesh->RemoveNode( N1[6] );
3363             if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3364               aMesh->RemoveNode( N2[6] );
3365           }
3366         }
3367         else if ( Ok13 )
3368         {
3369           mapEl_setLi.erase( tr3 );
3370           mapLi_listEl.erase( *link13 );
3371           if ( tr1->NbNodes() == 3 ) {
3372             const SMDS_MeshElement* newElem = 0;
3373             newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
3374             myLastCreatedElems.push_back(newElem);
3375             AddToSameGroups( newElem, tr1, aMesh );
3376             int aShapeId = tr1->getshapeId();
3377             if ( aShapeId )
3378               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3379             aMesh->RemoveElement( tr1 );
3380             aMesh->RemoveElement( tr3 );
3381           }
3382           else {
3383             vector< const SMDS_MeshNode* > N1;
3384             vector< const SMDS_MeshNode* > N2;
3385             getNodesFromTwoTria(tr1,tr3,N1,N2);
3386             // now we receive following N1 and N2 (using numeration as above image)
3387             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
3388             // i.e. first nodes from both arrays form a new diagonal
3389             const SMDS_MeshNode* aNodes[8];
3390             aNodes[0] = N1[0];
3391             aNodes[1] = N1[1];
3392             aNodes[2] = N2[0];
3393             aNodes[3] = N2[1];
3394             aNodes[4] = N1[3];
3395             aNodes[5] = N2[5];
3396             aNodes[6] = N2[3];
3397             aNodes[7] = N1[5];
3398             const SMDS_MeshElement* newElem = 0;
3399             if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3400               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3401                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3402             else
3403               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3404                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3405             myLastCreatedElems.push_back(newElem);
3406             AddToSameGroups( newElem, tr1, aMesh );
3407             int aShapeId = tr1->getshapeId();
3408             if ( aShapeId )
3409               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3410             aMesh->RemoveElement( tr1 );
3411             aMesh->RemoveElement( tr3 );
3412             // remove middle node (9)
3413             if ( N1[4]->NbInverseElements() == 0 )
3414               aMesh->RemoveNode( N1[4] );
3415             if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3416               aMesh->RemoveNode( N1[6] );
3417             if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3418               aMesh->RemoveNode( N2[6] );
3419           }
3420         }
3421
3422         // Next element to fuse: the rejected one
3423         if ( tr3 )
3424           startElem = Ok12 ? tr3 : tr2;
3425
3426       } // if ( startElem )
3427     } // while ( startElem || !startLinks.empty() )
3428   } // while ( ! mapEl_setLi.empty() )
3429
3430   return true;
3431 }
3432
3433 //================================================================================
3434 /*!
3435  * \brief Return nodes linked to the given one
3436  * \param theNode - the node
3437  * \param linkedNodes - the found nodes
3438  * \param type - the type of elements to check
3439  *
3440  * Medium nodes are ignored
3441  */
3442 //================================================================================
3443
3444 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
3445                                        TIDSortedElemSet &   linkedNodes,
3446                                        SMDSAbs_ElementType  type )
3447 {
3448   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
3449   while ( elemIt->more() )
3450   {
3451     const SMDS_MeshElement* elem = elemIt->next();
3452     if(elem->GetType() == SMDSAbs_0DElement)
3453       continue;
3454
3455     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
3456     if ( elem->GetType() == SMDSAbs_Volume )
3457     {
3458       SMDS_VolumeTool vol( elem );
3459       while ( nodeIt->more() ) {
3460         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3461         if ( theNode != n && vol.IsLinked( theNode, n ))
3462           linkedNodes.insert( n );
3463       }
3464     }
3465     else
3466     {
3467       for ( int i = 0; nodeIt->more(); ++i ) {
3468         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3469         if ( n == theNode ) {
3470           int iBefore = i - 1;
3471           int iAfter  = i + 1;
3472           if ( elem->IsQuadratic() ) {
3473             int nb = elem->NbNodes() / 2;
3474             iAfter  = SMESH_MesherHelper::WrapIndex( iAfter, nb );
3475             iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
3476           }
3477           linkedNodes.insert( elem->GetNodeWrap( iAfter ));
3478           linkedNodes.insert( elem->GetNodeWrap( iBefore ));
3479         }
3480       }
3481     }
3482   }
3483 }
3484
3485 //=======================================================================
3486 //function : laplacianSmooth
3487 //purpose  : pulls theNode toward the center of surrounding nodes directly
3488 //           connected to that node along an element edge
3489 //=======================================================================
3490
3491 void laplacianSmooth(const SMDS_MeshNode*                 theNode,
3492                      const Handle(Geom_Surface)&          theSurface,
3493                      map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3494 {
3495   // find surrounding nodes
3496
3497   TIDSortedElemSet nodeSet;
3498   SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
3499
3500   // compute new coodrs
3501
3502   double coord[] = { 0., 0., 0. };
3503   TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3504   for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
3505     const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3506     if ( theSurface.IsNull() ) { // smooth in 3D
3507       coord[0] += node->X();
3508       coord[1] += node->Y();
3509       coord[2] += node->Z();
3510     }
3511     else { // smooth in 2D
3512       ASSERT( theUVMap.find( node ) != theUVMap.end() );
3513       gp_XY* uv = theUVMap[ node ];
3514       coord[0] += uv->X();
3515       coord[1] += uv->Y();
3516     }
3517   }
3518   int nbNodes = nodeSet.size();
3519   if ( !nbNodes )
3520     return;
3521   coord[0] /= nbNodes;
3522   coord[1] /= nbNodes;
3523
3524   if ( !theSurface.IsNull() ) {
3525     ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3526     theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3527     gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3528     coord[0] = p3d.X();
3529     coord[1] = p3d.Y();
3530     coord[2] = p3d.Z();
3531   }
3532   else
3533     coord[2] /= nbNodes;
3534
3535   // move node
3536
3537   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3538 }
3539
3540 //=======================================================================
3541 //function : centroidalSmooth
3542 //purpose  : pulls theNode toward the element-area-weighted centroid of the
3543 //           surrounding elements
3544 //=======================================================================
3545
3546 void centroidalSmooth(const SMDS_MeshNode*                 theNode,
3547                       const Handle(Geom_Surface)&          theSurface,
3548                       map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3549 {
3550   gp_XYZ aNewXYZ(0.,0.,0.);
3551   SMESH::Controls::Area anAreaFunc;
3552   double totalArea = 0.;
3553   int nbElems = 0;
3554
3555   // compute new XYZ
3556
3557   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3558   while ( elemIt->more() )
3559   {
3560     const SMDS_MeshElement* elem = elemIt->next();
3561     nbElems++;
3562
3563     gp_XYZ elemCenter(0.,0.,0.);
3564     SMESH::Controls::TSequenceOfXYZ aNodePoints;
3565     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3566     int nn = elem->NbNodes();
3567     if(elem->IsQuadratic()) nn = nn/2;
3568     int i=0;
3569     //while ( itN->more() ) {
3570     while ( i<nn ) {
3571       const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3572       i++;
3573       gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3574       aNodePoints.push_back( aP );
3575       if ( !theSurface.IsNull() ) { // smooth in 2D
3576         ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3577         gp_XY* uv = theUVMap[ aNode ];
3578         aP.SetCoord( uv->X(), uv->Y(), 0. );
3579       }
3580       elemCenter += aP;
3581     }
3582     double elemArea = anAreaFunc.GetValue( aNodePoints );
3583     totalArea += elemArea;
3584     elemCenter /= nn;
3585     aNewXYZ += elemCenter * elemArea;
3586   }
3587   aNewXYZ /= totalArea;
3588   if ( !theSurface.IsNull() ) {
3589     theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3590     aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3591   }
3592
3593   // move node
3594
3595   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3596 }
3597
3598 //=======================================================================
3599 //function : getClosestUV
3600 //purpose  : return UV of closest projection
3601 //=======================================================================
3602
3603 static bool getClosestUV (Extrema_GenExtPS& projector,
3604                           const gp_Pnt&     point,
3605                           gp_XY &           result)
3606 {
3607   projector.Perform( point );
3608   if ( projector.IsDone() ) {
3609     double u, v, minVal = DBL_MAX;
3610     for ( int i = projector.NbExt(); i > 0; i-- )
3611       if ( projector.SquareDistance( i ) < minVal ) {
3612         minVal = projector.SquareDistance( i );
3613         projector.Point( i ).Parameter( u, v );
3614       }
3615     result.SetCoord( u, v );
3616     return true;
3617   }
3618   return false;
3619 }
3620
3621 //=======================================================================
3622 //function : Smooth
3623 //purpose  : Smooth theElements during theNbIterations or until a worst
3624 //           element has aspect ratio <= theTgtAspectRatio.
3625 //           Aspect Ratio varies in range [1.0, inf].
3626 //           If theElements is empty, the whole mesh is smoothed.
3627 //           theFixedNodes contains additionally fixed nodes. Nodes built
3628 //           on edges and boundary nodes are always fixed.
3629 //=======================================================================
3630
3631 void SMESH_MeshEditor::Smooth (TIDSortedElemSet &          theElems,
3632                                set<const SMDS_MeshNode*> & theFixedNodes,
3633                                const SmoothMethod          theSmoothMethod,
3634                                const int                   theNbIterations,
3635                                double                      theTgtAspectRatio,
3636                                const bool                  the2D)
3637 {
3638   ClearLastCreated();
3639
3640   if ( theTgtAspectRatio < 1.0 )
3641     theTgtAspectRatio = 1.0;
3642
3643   const double disttol = 1.e-16;
3644
3645   SMESH::Controls::AspectRatio aQualityFunc;
3646
3647   SMESHDS_Mesh* aMesh = GetMeshDS();
3648
3649   if ( theElems.empty() ) {
3650     // add all faces to theElems
3651     SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3652     while ( fIt->more() ) {
3653       const SMDS_MeshElement* face = fIt->next();
3654       theElems.insert( theElems.end(), face );
3655     }
3656   }
3657   // get all face ids theElems are on
3658   set< int > faceIdSet;
3659   TIDSortedElemSet::iterator itElem;
3660   if ( the2D )
3661     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3662       int fId = FindShape( *itElem );
3663       // check that corresponding submesh exists and a shape is face
3664       if (fId &&
3665           faceIdSet.find( fId ) == faceIdSet.end() &&
3666           aMesh->MeshElements( fId )) {
3667         TopoDS_Shape F = aMesh->IndexToShape( fId );
3668         if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3669           faceIdSet.insert( fId );
3670       }
3671     }
3672   faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3673
3674   // ===============================================
3675   // smooth elements on each TopoDS_Face separately
3676   // ===============================================
3677
3678   SMESH_MesherHelper helper( *GetMesh() );
3679
3680   set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treat 0 fId at the end
3681   for ( ; fId != faceIdSet.rend(); ++fId )
3682   {
3683     // get face surface and submesh
3684     Handle(Geom_Surface) surface;
3685     SMESHDS_SubMesh* faceSubMesh = 0;
3686     TopoDS_Face face;
3687     double fToler2 = 0;
3688     double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3689     bool isUPeriodic = false, isVPeriodic = false;
3690     if ( *fId )
3691     {
3692       face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3693       surface = BRep_Tool::Surface( face );
3694       faceSubMesh = aMesh->MeshElements( *fId );
3695       fToler2 = BRep_Tool::Tolerance( face );
3696       fToler2 *= fToler2 * 10.;
3697       isUPeriodic = surface->IsUPeriodic();
3698       // if ( isUPeriodic )
3699       //   surface->UPeriod();
3700       isVPeriodic = surface->IsVPeriodic();
3701       // if ( isVPeriodic )
3702       //   surface->VPeriod();
3703       surface->Bounds( u1, u2, v1, v2 );
3704       helper.SetSubShape( face );
3705     }
3706     // ---------------------------------------------------------
3707     // for elements on a face, find movable and fixed nodes and
3708     // compute UV for them
3709     // ---------------------------------------------------------
3710     bool checkBoundaryNodes = false;
3711     bool isQuadratic = false;
3712     set<const SMDS_MeshNode*> setMovableNodes;
3713     map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
3714     list< gp_XY > listUV; // uvs the 2 uvMaps refer to
3715     list< const SMDS_MeshElement* > elemsOnFace;
3716
3717     Extrema_GenExtPS projector;
3718     GeomAdaptor_Surface surfAdaptor;
3719     if ( !surface.IsNull() ) {
3720       surfAdaptor.Load( surface );
3721       projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
3722     }
3723     int nbElemOnFace = 0;
3724     itElem = theElems.begin();
3725     // loop on not yet smoothed elements: look for elems on a face
3726     while ( itElem != theElems.end() )
3727     {
3728       if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
3729         break; // all elements found
3730
3731       const SMDS_MeshElement* elem = *itElem;
3732       if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
3733            ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
3734         ++itElem;
3735         continue;
3736       }
3737       elemsOnFace.push_back( elem );
3738       theElems.erase( itElem++ );
3739       nbElemOnFace++;
3740
3741       if ( !isQuadratic )
3742         isQuadratic = elem->IsQuadratic();
3743
3744       // get movable nodes of elem
3745       const SMDS_MeshNode* node;
3746       SMDS_TypeOfPosition posType;
3747       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3748       int nn = 0, nbn =  elem->NbNodes();
3749       if(elem->IsQuadratic())
3750         nbn = nbn/2;
3751       while ( nn++ < nbn ) {
3752         node = static_cast<const SMDS_MeshNode*>( itN->next() );
3753         const SMDS_PositionPtr& pos = node->GetPosition();
3754         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3755         if (posType != SMDS_TOP_EDGE &&
3756             posType != SMDS_TOP_VERTEX &&
3757             theFixedNodes.find( node ) == theFixedNodes.end())
3758         {
3759           // check if all faces around the node are on faceSubMesh
3760           // because a node on edge may be bound to face
3761           bool all = true;
3762           if ( faceSubMesh ) {
3763             SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3764             while ( eIt->more() && all ) {
3765               const SMDS_MeshElement* e = eIt->next();
3766               all = faceSubMesh->Contains( e );
3767             }
3768           }
3769           if ( all )
3770             setMovableNodes.insert( node );
3771           else
3772             checkBoundaryNodes = true;
3773         }
3774         if ( posType == SMDS_TOP_3DSPACE )
3775           checkBoundaryNodes = true;
3776       }
3777
3778       if ( surface.IsNull() )
3779         continue;
3780
3781       // get nodes to check UV
3782       list< const SMDS_MeshNode* > uvCheckNodes;
3783       const SMDS_MeshNode* nodeInFace = 0;
3784       itN = elem->nodesIterator();
3785       nn = 0; nbn =  elem->NbNodes();
3786       if(elem->IsQuadratic())
3787         nbn = nbn/2;
3788       while ( nn++ < nbn ) {
3789         node = static_cast<const SMDS_MeshNode*>( itN->next() );
3790         if ( node->GetPosition()->GetDim() == 2 )
3791           nodeInFace = node;
3792         if ( uvMap.find( node ) == uvMap.end() )
3793           uvCheckNodes.push_back( node );
3794         // add nodes of elems sharing node
3795         //         SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3796         //         while ( eIt->more() ) {
3797         //           const SMDS_MeshElement* e = eIt->next();
3798         //           if ( e != elem ) {
3799         //             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3800         //             while ( nIt->more() ) {
3801         //               const SMDS_MeshNode* n =
3802         //                 static_cast<const SMDS_MeshNode*>( nIt->next() );
3803         //               if ( uvMap.find( n ) == uvMap.end() )
3804         //                 uvCheckNodes.push_back( n );
3805         //             }
3806         //           }
3807         //         }
3808       }
3809       // check UV on face
3810       list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
3811       for ( ; n != uvCheckNodes.end(); ++n ) {
3812         node = *n;
3813         gp_XY uv( 0, 0 );
3814         const SMDS_PositionPtr& pos = node->GetPosition();
3815         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3816         // get existing UV
3817         if ( pos )
3818         {
3819           bool toCheck = true;
3820           uv = helper.GetNodeUV( face, node, nodeInFace, &toCheck );
3821         }
3822         // compute not existing UV
3823         bool project = ( posType == SMDS_TOP_3DSPACE );
3824         // double dist1 = DBL_MAX, dist2 = 0;
3825         // if ( posType != SMDS_TOP_3DSPACE ) {
3826         //   dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
3827         //   project = dist1 > fToler2;
3828         // }
3829         if ( project ) { // compute new UV
3830           gp_XY newUV;
3831           gp_Pnt pNode = SMESH_NodeXYZ( node );
3832           if ( !getClosestUV( projector, pNode, newUV )) {
3833             MESSAGE("Node Projection Failed " << node);
3834           }
3835           else {
3836             if ( isUPeriodic )
3837               newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
3838             if ( isVPeriodic )
3839               newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
3840             // check new UV
3841             // if ( posType != SMDS_TOP_3DSPACE )
3842             //   dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
3843             // if ( dist2 < dist1 )
3844             uv = newUV;
3845           }
3846         }
3847         // store UV in the map
3848         listUV.push_back( uv );
3849         uvMap.insert( make_pair( node, &listUV.back() ));
3850       }
3851     } // loop on not yet smoothed elements
3852
3853     if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
3854       checkBoundaryNodes = true;
3855
3856     // fix nodes on mesh boundary
3857
3858     if ( checkBoundaryNodes ) {
3859       map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
3860       map< SMESH_TLink, int >::iterator link_nb;
3861       // put all elements links to linkNbMap
3862       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3863       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3864         const SMDS_MeshElement* elem = (*elemIt);
3865         int nbn =  elem->NbCornerNodes();
3866         // loop on elem links: insert them in linkNbMap
3867         for ( int iN = 0; iN < nbn; ++iN ) {
3868           const SMDS_MeshNode* n1 = elem->GetNode( iN );
3869           const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
3870           SMESH_TLink link( n1, n2 );
3871           link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
3872           link_nb->second++;
3873         }
3874       }
3875       // remove nodes that are in links encountered only once from setMovableNodes
3876       for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
3877         if ( link_nb->second == 1 ) {
3878           setMovableNodes.erase( link_nb->first.node1() );
3879           setMovableNodes.erase( link_nb->first.node2() );
3880         }
3881       }
3882     }
3883
3884     // -----------------------------------------------------
3885     // for nodes on seam edge, compute one more UV ( uvMap2 );
3886     // find movable nodes linked to nodes on seam and which
3887     // are to be smoothed using the second UV ( uvMap2 )
3888     // -----------------------------------------------------
3889
3890     set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
3891     if ( !surface.IsNull() ) {
3892       TopExp_Explorer eExp( face, TopAbs_EDGE );
3893       for ( ; eExp.More(); eExp.Next() ) {
3894         TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
3895         if ( !BRep_Tool::IsClosed( edge, face ))
3896           continue;
3897         SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
3898         if ( !sm ) continue;
3899         // find out which parameter varies for a node on seam
3900         double f,l;
3901         gp_Pnt2d uv1, uv2;
3902         Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3903         if ( pcurve.IsNull() ) continue;
3904         uv1 = pcurve->Value( f );
3905         edge.Reverse();
3906         pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3907         if ( pcurve.IsNull() ) continue;
3908         uv2 = pcurve->Value( f );
3909         int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
3910         // assure uv1 < uv2
3911         if ( uv1.Coord( iPar ) > uv2.Coord( iPar ))
3912           std::swap( uv1, uv2 );
3913         // get nodes on seam and its vertices
3914         list< const SMDS_MeshNode* > seamNodes;
3915         SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
3916         while ( nSeamIt->more() ) {
3917           const SMDS_MeshNode* node = nSeamIt->next();
3918           if ( !isQuadratic || !IsMedium( node ))
3919             seamNodes.push_back( node );
3920         }
3921         TopExp_Explorer vExp( edge, TopAbs_VERTEX );
3922         for ( ; vExp.More(); vExp.Next() ) {
3923           sm = aMesh->MeshElements( vExp.Current() );
3924           if ( sm ) {
3925             nSeamIt = sm->GetNodes();
3926             while ( nSeamIt->more() )
3927               seamNodes.push_back( nSeamIt->next() );
3928           }
3929         }
3930         // loop on nodes on seam
3931         list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
3932         for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
3933           const SMDS_MeshNode* nSeam = *noSeIt;
3934           map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
3935           if ( n_uv == uvMap.end() )
3936             continue;
3937           // set the first UV
3938           n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
3939           // set the second UV
3940           listUV.push_back( *n_uv->second );
3941           listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
3942           if ( uvMap2.empty() )
3943             uvMap2 = uvMap; // copy the uvMap contents
3944           uvMap2[ nSeam ] = &listUV.back();
3945
3946           // collect movable nodes linked to ones on seam in nodesNearSeam
3947           SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
3948           while ( eIt->more() ) {
3949             const SMDS_MeshElement* e = eIt->next();
3950             int nbUseMap1 = 0, nbUseMap2 = 0;
3951             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3952             int nn = 0, nbn =  e->NbNodes();
3953             if(e->IsQuadratic()) nbn = nbn/2;
3954             while ( nn++ < nbn )
3955             {
3956               const SMDS_MeshNode* n =
3957                 static_cast<const SMDS_MeshNode*>( nIt->next() );
3958               if (n == nSeam ||
3959                   setMovableNodes.find( n ) == setMovableNodes.end() )
3960                 continue;
3961               // add only nodes being closer to uv2 than to uv1
3962               // gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
3963               //              0.5 * ( n->Y() + nSeam->Y() ),
3964               //              0.5 * ( n->Z() + nSeam->Z() ));
3965               // gp_XY uv;
3966               // getClosestUV( projector, pMid, uv );
3967               double x = uvMap[ n ]->Coord( iPar );
3968               if ( Abs( uv1.Coord( iPar ) - x ) >
3969                    Abs( uv2.Coord( iPar ) - x )) {
3970                 nodesNearSeam.insert( n );
3971                 nbUseMap2++;
3972               }
3973               else
3974                 nbUseMap1++;
3975             }
3976             // for centroidalSmooth all element nodes must
3977             // be on one side of a seam
3978             if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
3979               SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3980               nn = 0;
3981               while ( nn++ < nbn ) {
3982                 const SMDS_MeshNode* n =
3983                   static_cast<const SMDS_MeshNode*>( nIt->next() );
3984                 setMovableNodes.erase( n );
3985               }
3986             }
3987           }
3988         } // loop on nodes on seam
3989       } // loop on edge of a face
3990     } // if ( !face.IsNull() )
3991
3992     if ( setMovableNodes.empty() ) {
3993       MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
3994       continue; // goto next face
3995     }
3996
3997     // -------------
3998     // SMOOTHING //
3999     // -------------
4000
4001     int it = -1;
4002     double maxRatio = -1., maxDisplacement = -1.;
4003     set<const SMDS_MeshNode*>::iterator nodeToMove;
4004     for ( it = 0; it < theNbIterations; it++ ) {
4005       maxDisplacement = 0.;
4006       nodeToMove = setMovableNodes.begin();
4007       for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4008         const SMDS_MeshNode* node = (*nodeToMove);
4009         gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
4010
4011         // smooth
4012         bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
4013         if ( theSmoothMethod == LAPLACIAN )
4014           laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
4015         else
4016           centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
4017
4018         // node displacement
4019         gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
4020         Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
4021         if ( aDispl > maxDisplacement )
4022           maxDisplacement = aDispl;
4023       }
4024       // no node movement => exit
4025       //if ( maxDisplacement < 1.e-16 ) {
4026       if ( maxDisplacement < disttol ) {
4027         MESSAGE("-- no node movement --");
4028         break;
4029       }
4030
4031       // check elements quality
4032       maxRatio  = 0;
4033       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4034       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4035         const SMDS_MeshElement* elem = (*elemIt);
4036         if ( !elem || elem->GetType() != SMDSAbs_Face )
4037           continue;
4038         SMESH::Controls::TSequenceOfXYZ aPoints;
4039         if ( aQualityFunc.GetPoints( elem, aPoints )) {
4040           double aValue = aQualityFunc.GetValue( aPoints );
4041           if ( aValue > maxRatio )
4042             maxRatio = aValue;
4043         }
4044       }
4045       if ( maxRatio <= theTgtAspectRatio ) {
4046         //MESSAGE("-- quality achieved --");
4047         break;
4048       }
4049       if (it+1 == theNbIterations) {
4050         //MESSAGE("-- Iteration limit exceeded --");
4051       }
4052     } // smoothing iterations
4053
4054     // MESSAGE(" Face id: " << *fId <<
4055     //         " Nb iterstions: " << it <<
4056     //         " Displacement: " << maxDisplacement <<
4057     //         " Aspect Ratio " << maxRatio);
4058
4059     // ---------------------------------------
4060     // new nodes positions are computed,
4061     // record movement in DS and set new UV
4062     // ---------------------------------------
4063     nodeToMove = setMovableNodes.begin();
4064     for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4065       SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
4066       aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
4067       map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
4068       if ( node_uv != uvMap.end() ) {
4069         gp_XY* uv = node_uv->second;
4070         node->SetPosition
4071           ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
4072       }
4073     }
4074
4075     // move medium nodes of quadratic elements
4076     if ( isQuadratic )
4077     {
4078       vector<const SMDS_MeshNode*> nodes;
4079       bool checkUV;
4080       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4081       for ( ; elemIt != elemsOnFace.end(); ++elemIt )
4082       {
4083         const SMDS_MeshElement* QF = *elemIt;
4084         if ( QF->IsQuadratic() )
4085         {
4086           nodes.assign( SMDS_MeshElement::iterator( QF->interlacedNodesIterator() ),
4087                         SMDS_MeshElement::iterator() );
4088           nodes.push_back( nodes[0] );
4089           gp_Pnt xyz;
4090           for (size_t i = 1; i < nodes.size(); i += 2 ) // i points to a medium node
4091           {
4092             if ( !surface.IsNull() )
4093             {
4094               gp_XY uv1 = helper.GetNodeUV( face, nodes[i-1], nodes[i+1], &checkUV );
4095               gp_XY uv2 = helper.GetNodeUV( face, nodes[i+1], nodes[i-1], &checkUV );
4096               gp_XY uv  = helper.GetMiddleUV( surface, uv1, uv2 );
4097               xyz = surface->Value( uv.X(), uv.Y() );
4098             }
4099             else {
4100               xyz = 0.5 * ( SMESH_NodeXYZ( nodes[i-1] ) + SMESH_NodeXYZ( nodes[i+1] ));
4101             }
4102             if (( SMESH_NodeXYZ( nodes[i] ) - xyz.XYZ() ).Modulus() > disttol )
4103               // we have to move a medium node
4104               aMesh->MoveNode( nodes[i], xyz.X(), xyz.Y(), xyz.Z() );
4105           }
4106         }
4107       }
4108     }
4109
4110   } // loop on face ids
4111
4112 }
4113
4114 namespace
4115 {
4116   //=======================================================================
4117   //function : isReverse
4118   //purpose  : Return true if normal of prevNodes is not co-directied with
4119   //           gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
4120   //           iNotSame is where prevNodes and nextNodes are different.
4121   //           If result is true then future volume orientation is OK
4122   //=======================================================================
4123
4124   bool isReverse(const SMDS_MeshElement*             face,
4125                  const vector<const SMDS_MeshNode*>& prevNodes,
4126                  const vector<const SMDS_MeshNode*>& nextNodes,
4127                  const int                           iNotSame)
4128   {
4129
4130     SMESH_NodeXYZ pP = prevNodes[ iNotSame ];
4131     SMESH_NodeXYZ pN = nextNodes[ iNotSame ];
4132     gp_XYZ extrDir( pN - pP ), faceNorm;
4133     SMESH_MeshAlgos::FaceNormal( face, faceNorm, /*normalized=*/false );
4134
4135     return faceNorm * extrDir < 0.0;
4136   }
4137
4138   //================================================================================
4139   /*!
4140    * \brief Assure that theElemSets[0] holds elements, not nodes
4141    */
4142   //================================================================================
4143
4144   void setElemsFirst( TIDSortedElemSet theElemSets[2] )
4145   {
4146     if ( !theElemSets[0].empty() &&
4147          (*theElemSets[0].begin())->GetType() == SMDSAbs_Node )
4148     {
4149       std::swap( theElemSets[0], theElemSets[1] );
4150     }
4151     else if ( !theElemSets[1].empty() &&
4152               (*theElemSets[1].begin())->GetType() != SMDSAbs_Node )
4153     {
4154       std::swap( theElemSets[0], theElemSets[1] );
4155     }
4156   }
4157 }
4158
4159 //=======================================================================
4160 /*!
4161  * \brief Create elements by sweeping an element
4162  * \param elem - element to sweep
4163  * \param newNodesItVec - nodes generated from each node of the element
4164  * \param newElems - generated elements
4165  * \param nbSteps - number of sweeping steps
4166  * \param srcElements - to append elem for each generated element
4167  */
4168 //=======================================================================
4169
4170 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement*               elem,
4171                                     const vector<TNodeOfNodeListMapItr> & newNodesItVec,
4172                                     list<const SMDS_MeshElement*>&        newElems,
4173                                     const size_t                          nbSteps,
4174                                     SMESH_SequenceOfElemPtr&              srcElements)
4175 {
4176   SMESHDS_Mesh* aMesh = GetMeshDS();
4177
4178   const int           nbNodes = elem->NbNodes();
4179   const int         nbCorners = elem->NbCornerNodes();
4180   SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
4181                                                           polyhedron creation !!! */
4182   // Loop on elem nodes:
4183   // find new nodes and detect same nodes indices
4184   vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
4185   vector<const SMDS_MeshNode*> prevNod( nbNodes );
4186   vector<const SMDS_MeshNode*> nextNod( nbNodes );
4187   vector<const SMDS_MeshNode*> midlNod( nbNodes );
4188
4189   int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
4190   vector<int> sames(nbNodes);
4191   vector<bool> isSingleNode(nbNodes);
4192
4193   for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4194     TNodeOfNodeListMapItr                        nnIt = newNodesItVec[ iNode ];
4195     const SMDS_MeshNode*                         node = nnIt->first;
4196     const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
4197     if ( listNewNodes.empty() )
4198       return;
4199
4200     itNN   [ iNode ] = listNewNodes.begin();
4201     prevNod[ iNode ] = node;
4202     nextNod[ iNode ] = listNewNodes.front();
4203
4204     isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
4205                                                              corner node of linear */
4206     if ( prevNod[ iNode ] != nextNod [ iNode ])
4207       nbDouble += !isSingleNode[iNode];
4208
4209     if( iNode < nbCorners ) { // check corners only
4210       if ( prevNod[ iNode ] == nextNod [ iNode ])
4211         sames[nbSame++] = iNode;
4212       else
4213         iNotSameNode = iNode;
4214     }
4215   }
4216
4217   if ( nbSame == nbNodes || nbSame > 2) {
4218     MESSAGE( " Too many same nodes of element " << elem->GetID() );
4219     return;
4220   }
4221
4222   if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
4223   {
4224     // fix nodes order to have bottom normal external
4225     if ( baseType == SMDSEntity_Polygon )
4226     {
4227       std::reverse( itNN.begin(), itNN.end() );
4228       std::reverse( prevNod.begin(), prevNod.end() );
4229       std::reverse( midlNod.begin(), midlNod.end() );
4230       std::reverse( nextNod.begin(), nextNod.end() );
4231       std::reverse( isSingleNode.begin(), isSingleNode.end() );
4232     }
4233     else
4234     {
4235       const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType, nbNodes );
4236       SMDS_MeshCell::applyInterlace( ind, itNN );
4237       SMDS_MeshCell::applyInterlace( ind, prevNod );
4238       SMDS_MeshCell::applyInterlace( ind, nextNod );
4239       SMDS_MeshCell::applyInterlace( ind, midlNod );
4240       SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4241       if ( nbSame > 0 )
4242       {
4243         sames[nbSame] = iNotSameNode;
4244         for ( int j = 0; j <= nbSame; ++j )
4245           for ( size_t i = 0; i < ind.size(); ++i )
4246             if ( ind[i] == sames[j] )
4247             {
4248               sames[j] = i;
4249               break;
4250             }
4251         iNotSameNode = sames[nbSame];
4252       }
4253     }
4254   }
4255   else if ( elem->GetType() == SMDSAbs_Edge )
4256   {
4257     // orient a new face same as adjacent one
4258     int i1, i2;
4259     const SMDS_MeshElement* e;
4260     TIDSortedElemSet dummy;
4261     if (( e = SMESH_MeshAlgos::FindFaceInSet( nextNod[0], prevNod[0], dummy,dummy, &i1, &i2 )) ||
4262         ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[1], nextNod[1], dummy,dummy, &i1, &i2 )) ||
4263         ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[0], prevNod[1], dummy,dummy, &i1, &i2 )))
4264     {
4265       // there is an adjacent face, check order of nodes in it
4266       bool sameOrder = ( Abs( i2 - i1 ) == 1 ) ? ( i2 > i1 ) : ( i2 < i1 );
4267       if ( sameOrder )
4268       {
4269         std::swap( itNN[0],    itNN[1] );
4270         std::swap( prevNod[0], prevNod[1] );
4271         std::swap( nextNod[0], nextNod[1] );
4272         std::swap( isSingleNode[0], isSingleNode[1] );
4273         if ( nbSame > 0 )
4274           sames[0] = 1 - sames[0];
4275         iNotSameNode = 1 - iNotSameNode;
4276       }
4277     }
4278   }
4279
4280   int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
4281   if ( nbSame > 0 ) {
4282     iSameNode    = sames[ nbSame-1 ];
4283     iBeforeSame  = ( iSameNode + nbCorners - 1 ) % nbCorners;
4284     iAfterSame   = ( iSameNode + 1 ) % nbCorners;
4285     iOpposSame   = ( iSameNode - 2 < 0  ? iSameNode + 2 : iSameNode - 2 );
4286   }
4287
4288   if ( baseType == SMDSEntity_Polygon )
4289   {
4290     if      ( nbNodes == 3 ) baseType = SMDSEntity_Triangle;
4291     else if ( nbNodes == 4 ) baseType = SMDSEntity_Quadrangle;
4292   }
4293   else if ( baseType == SMDSEntity_Quad_Polygon )
4294   {
4295     if      ( nbNodes == 6 ) baseType = SMDSEntity_Quad_Triangle;
4296     else if ( nbNodes == 8 ) baseType = SMDSEntity_Quad_Quadrangle;
4297   }
4298
4299   // make new elements
4300   for ( size_t iStep = 0; iStep < nbSteps; iStep++ )
4301   {
4302     // get next nodes
4303     for ( iNode = 0; iNode < nbNodes; iNode++ )
4304     {
4305       midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
4306       nextNod[ iNode ] = *itNN[ iNode ]++;
4307     }
4308
4309     SMDS_MeshElement* aNewElem = 0;
4310     /*if(!elem->IsPoly())*/ {
4311       switch ( baseType ) {
4312       case SMDSEntity_0D:
4313       case SMDSEntity_Node: { // sweep NODE
4314         if ( nbSame == 0 ) {
4315           if ( isSingleNode[0] )
4316             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
4317           else
4318             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
4319         }
4320         else
4321           return;
4322         break;
4323       }
4324       case SMDSEntity_Edge: { // sweep EDGE
4325         if ( nbDouble == 0 )
4326         {
4327           if ( nbSame == 0 ) // ---> quadrangle
4328             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4329                                       nextNod[ 1 ], nextNod[ 0 ] );
4330           else               // ---> triangle
4331             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4332                                       nextNod[ iNotSameNode ] );
4333         }
4334         else                 // ---> polygon
4335         {
4336           vector<const SMDS_MeshNode*> poly_nodes;
4337           poly_nodes.push_back( prevNod[0] );
4338           poly_nodes.push_back( prevNod[1] );
4339           if ( prevNod[1] != nextNod[1] )
4340           {
4341             if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
4342             poly_nodes.push_back( nextNod[1] );
4343           }
4344           if ( prevNod[0] != nextNod[0] )
4345           {
4346             poly_nodes.push_back( nextNod[0] );
4347             if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
4348           }
4349           switch ( poly_nodes.size() ) {
4350           case 3:
4351             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
4352             break;
4353           case 4:
4354             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
4355                                        poly_nodes[ 2 ], poly_nodes[ 3 ]);
4356             break;
4357           default:
4358             aNewElem = aMesh->AddPolygonalFace (poly_nodes);
4359           }
4360         }
4361         break;
4362       }
4363       case SMDSEntity_Triangle: // TRIANGLE --->
4364       {
4365         if ( nbDouble > 0 ) break;
4366         if ( nbSame == 0 )       // ---> pentahedron
4367           aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4368                                        nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
4369
4370         else if ( nbSame == 1 )  // ---> pyramid
4371           aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4372                                        nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
4373                                        nextNod[ iSameNode ]);
4374
4375         else // 2 same nodes:       ---> tetrahedron
4376           aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4377                                        nextNod[ iNotSameNode ]);
4378         break;
4379       }
4380       case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
4381       {
4382         if ( nbSame == 2 )
4383           return;
4384         if ( nbDouble+nbSame == 2 )
4385         {
4386           if(nbSame==0) {      // ---> quadratic quadrangle
4387             aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4388                                       prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
4389           }
4390           else { //(nbSame==1) // ---> quadratic triangle
4391             if(sames[0]==2) {
4392               return; // medium node on axis
4393             }
4394             else if(sames[0]==0)
4395               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1],
4396                                         prevNod[2], midlNod[1], nextNod[2] );
4397             else // sames[0]==1
4398               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[0],
4399                                         prevNod[2], nextNod[2], midlNod[0]);
4400           }
4401         }
4402         else if ( nbDouble == 3 )
4403         {
4404           if ( nbSame == 0 ) {  // ---> bi-quadratic quadrangle
4405             aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4406                                       prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
4407           }
4408         }
4409         else
4410           return;
4411         break;
4412       }
4413       case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
4414         if ( nbDouble > 0 ) break;
4415
4416         if ( nbSame == 0 )       // ---> hexahedron
4417           aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
4418                                        nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
4419
4420         else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
4421           aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4422                                        nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
4423                                        nextNod[ iSameNode ]);
4424           newElems.push_back( aNewElem );
4425           aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ],  prevNod[ iOpposSame ],
4426                                        prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
4427                                        nextNod[ iOpposSame ],  nextNod[ iBeforeSame ] );
4428         }
4429         else if ( nbSame == 2 ) { // ---> pentahedron
4430           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
4431             // iBeforeSame is same too
4432             aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
4433                                          nextNod[ iOpposSame ],  prevNod[ iSameNode ],
4434                                          prevNod[ iAfterSame ],  nextNod[ iAfterSame ]);
4435           else
4436             // iAfterSame is same too
4437             aNewElem = aMesh->AddVolume (prevNod[ iSameNode ],   prevNod[ iBeforeSame ],
4438                                          nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
4439                                          prevNod[ iOpposSame ],  nextNod[ iOpposSame ]);
4440         }
4441         break;
4442       }
4443       case SMDSEntity_Quad_Triangle:  // sweep (Bi)Quadratic TRIANGLE --->
4444       case SMDSEntity_BiQuad_Triangle: /* ??? */ {
4445         if ( nbDouble+nbSame != 3 ) break;
4446         if(nbSame==0) {
4447           // --->  pentahedron with 15 nodes
4448           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4449                                        nextNod[0], nextNod[1], nextNod[2],
4450                                        prevNod[3], prevNod[4], prevNod[5],
4451                                        nextNod[3], nextNod[4], nextNod[5],
4452                                        midlNod[0], midlNod[1], midlNod[2]);
4453         }
4454         else if(nbSame==1) {
4455           // --->  2d order pyramid of 13 nodes
4456           int apex = iSameNode;
4457           int i0 = ( apex + 1 ) % nbCorners;
4458           int i1 = ( apex - 1 + nbCorners ) % nbCorners;
4459           int i0a = apex + 3;
4460           int i1a = i1 + 3;
4461           int i01 = i0 + 3;
4462           aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
4463                                       nextNod[i0], nextNod[i1], prevNod[apex],
4464                                       prevNod[i01], midlNod[i0],
4465                                       nextNod[i01], midlNod[i1],
4466                                       prevNod[i1a], prevNod[i0a],
4467                                       nextNod[i0a], nextNod[i1a]);
4468         }
4469         else if(nbSame==2) {
4470           // --->  2d order tetrahedron of 10 nodes
4471           int n1 = iNotSameNode;
4472           int n2 = ( n1 + 1             ) % nbCorners;
4473           int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
4474           int n12 = n1 + 3;
4475           int n23 = n2 + 3;
4476           int n31 = n3 + 3;
4477           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
4478                                        prevNod[n12], prevNod[n23], prevNod[n31],
4479                                        midlNod[n1], nextNod[n12], nextNod[n31]);
4480         }
4481         break;
4482       }
4483       case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
4484         if( nbSame == 0 ) {
4485           if ( nbDouble != 4 ) break;
4486           // --->  hexahedron with 20 nodes
4487           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4488                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4489                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4490                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4491                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
4492         }
4493         else if(nbSame==1) {
4494           // ---> pyramid + pentahedron - can not be created since it is needed
4495           // additional middle node at the center of face
4496           //INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
4497           return;
4498         }
4499         else if( nbSame == 2 ) {
4500           if ( nbDouble != 2 ) break;
4501           // --->  2d order Pentahedron with 15 nodes
4502           int n1,n2,n4,n5;
4503           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
4504             // iBeforeSame is same too
4505             n1 = iBeforeSame;
4506             n2 = iOpposSame;
4507             n4 = iSameNode;
4508             n5 = iAfterSame;
4509           }
4510           else {
4511             // iAfterSame is same too
4512             n1 = iSameNode;
4513             n2 = iBeforeSame;
4514             n4 = iAfterSame;
4515             n5 = iOpposSame;
4516           }
4517           int n12 = n2 + 4;
4518           int n45 = n4 + 4;
4519           int n14 = n1 + 4;
4520           int n25 = n5 + 4;
4521           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
4522                                        prevNod[n4], prevNod[n5], nextNod[n5],
4523                                        prevNod[n12], midlNod[n2], nextNod[n12],
4524                                        prevNod[n45], midlNod[n5], nextNod[n45],
4525                                        prevNod[n14], prevNod[n25], nextNod[n25]);
4526         }
4527         break;
4528       }
4529       case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
4530
4531         if( nbSame == 0 && nbDouble == 9 ) {
4532           // --->  tri-quadratic hexahedron with 27 nodes
4533           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4534                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4535                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4536                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4537                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4538                                        prevNod[8], // bottom center
4539                                        midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4540                                        nextNod[8], // top center
4541                                        midlNod[8]);// elem center
4542         }
4543         else
4544         {
4545           return;
4546         }
4547         break;
4548       }
4549       case SMDSEntity_Polygon: { // sweep POLYGON
4550
4551         if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4552           // --->  hexagonal prism
4553           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4554                                        prevNod[3], prevNod[4], prevNod[5],
4555                                        nextNod[0], nextNod[1], nextNod[2],
4556                                        nextNod[3], nextNod[4], nextNod[5]);
4557         }
4558         break;
4559       }
4560       case SMDSEntity_Ball:
4561         return;
4562
4563       default:
4564         break;
4565       } // switch ( baseType )
4566     } // scope
4567
4568     if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4569     {
4570       if ( baseType != SMDSEntity_Polygon )
4571       {
4572         const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType,nbNodes);
4573         SMDS_MeshCell::applyInterlace( ind, prevNod );
4574         SMDS_MeshCell::applyInterlace( ind, nextNod );
4575         SMDS_MeshCell::applyInterlace( ind, midlNod );
4576         SMDS_MeshCell::applyInterlace( ind, itNN );
4577         SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4578         baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4579       }
4580       vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4581       vector<int> quantities (nbNodes + 2);
4582       polyedre_nodes.clear();
4583       quantities.clear();
4584
4585       // bottom of prism
4586       for (int inode = 0; inode < nbNodes; inode++)
4587         polyedre_nodes.push_back( prevNod[inode] );
4588       quantities.push_back( nbNodes );
4589
4590       // top of prism
4591       polyedre_nodes.push_back( nextNod[0] );
4592       for (int inode = nbNodes; inode-1; --inode )
4593         polyedre_nodes.push_back( nextNod[inode-1] );
4594       quantities.push_back( nbNodes );
4595
4596       // side faces
4597       // 3--6--2
4598       // |     |
4599       // 7     5
4600       // |     |
4601       // 0--4--1
4602       const int iQuad = elem->IsQuadratic();
4603       for (int iface = 0; iface < nbNodes; iface += 1+iQuad )
4604       {
4605         const int prevNbNodes = polyedre_nodes.size(); // to detect degenerated face
4606         int inextface = (iface+1+iQuad) % nbNodes;
4607         int imid      = (iface+1) % nbNodes;
4608         polyedre_nodes.push_back( prevNod[inextface] );         // 0
4609         if ( iQuad ) polyedre_nodes.push_back( prevNod[imid] ); // 4
4610         polyedre_nodes.push_back( prevNod[iface] );             // 1
4611         if ( prevNod[iface] != nextNod[iface] ) // 1 != 2
4612         {
4613           if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]); // 5
4614           polyedre_nodes.push_back( nextNod[iface] );                         // 2
4615         }
4616         if ( iQuad ) polyedre_nodes.push_back( nextNod[imid] );               // 6
4617         if ( prevNod[inextface] != nextNod[inextface] ) // 0 != 3
4618         {
4619           polyedre_nodes.push_back( nextNod[inextface] );                            // 3
4620           if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);// 7
4621         }
4622         const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4623         if ( nbFaceNodes > 2 )
4624           quantities.push_back( nbFaceNodes );
4625         else // degenerated face
4626           polyedre_nodes.resize( prevNbNodes );
4627       }
4628       aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4629
4630     } // try to create a polyherdal prism
4631
4632     if ( aNewElem ) {
4633       newElems.push_back( aNewElem );
4634       myLastCreatedElems.push_back(aNewElem);
4635       srcElements.push_back( elem );
4636     }
4637
4638     // set new prev nodes
4639     for ( iNode = 0; iNode < nbNodes; iNode++ )
4640       prevNod[ iNode ] = nextNod[ iNode ];
4641
4642   } // loop on steps
4643 }
4644
4645 //=======================================================================
4646 /*!
4647  * \brief Create 1D and 2D elements around swept elements
4648  * \param mapNewNodes - source nodes and ones generated from them
4649  * \param newElemsMap - source elements and ones generated from them
4650  * \param elemNewNodesMap - nodes generated from each node of each element
4651  * \param elemSet - all swept elements
4652  * \param nbSteps - number of sweeping steps
4653  * \param srcElements - to append elem for each generated element
4654  */
4655 //=======================================================================
4656
4657 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap &     mapNewNodes,
4658                                   TTElemOfElemListMap &    newElemsMap,
4659                                   TElemOfVecOfNnlmiMap &   elemNewNodesMap,
4660                                   TIDSortedElemSet&        elemSet,
4661                                   const int                nbSteps,
4662                                   SMESH_SequenceOfElemPtr& srcElements)
4663 {
4664   ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4665   SMESHDS_Mesh* aMesh = GetMeshDS();
4666
4667   // Find nodes belonging to only one initial element - sweep them into edges.
4668
4669   TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4670   for ( ; nList != mapNewNodes.end(); nList++ )
4671   {
4672     const SMDS_MeshNode* node =
4673       static_cast<const SMDS_MeshNode*>( nList->first );
4674     if ( newElemsMap.count( node ))
4675       continue; // node was extruded into edge
4676     SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4677     int nbInitElems = 0;
4678     const SMDS_MeshElement* el = 0;
4679     SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4680     while ( eIt->more() && nbInitElems < 2 ) {
4681       const SMDS_MeshElement* e = eIt->next();
4682       SMDSAbs_ElementType  type = e->GetType();
4683       if ( type == SMDSAbs_Volume ||
4684            type < highType ||
4685            !elemSet.count(e))
4686         continue;
4687       if ( type > highType ) {
4688         nbInitElems = 0;
4689         highType    = type;
4690       }
4691       el = e;
4692       ++nbInitElems;
4693     }
4694     if ( nbInitElems == 1 ) {
4695       bool NotCreateEdge = el && el->IsMediumNode(node);
4696       if(!NotCreateEdge) {
4697         vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
4698         list<const SMDS_MeshElement*> newEdges;
4699         sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
4700       }
4701     }
4702   }
4703
4704   // Make a ceiling for each element ie an equal element of last new nodes.
4705   // Find free links of faces - make edges and sweep them into faces.
4706
4707   ElemFeatures polyFace( SMDSAbs_Face, /*isPoly=*/true ), anyFace;
4708
4709   TTElemOfElemListMap::iterator  itElem      = newElemsMap.begin();
4710   TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
4711   for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
4712   {
4713     const SMDS_MeshElement* elem = itElem->first;
4714     vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
4715
4716     if(itElem->second.size()==0) continue;
4717
4718     const bool isQuadratic = elem->IsQuadratic();
4719
4720     if ( elem->GetType() == SMDSAbs_Edge ) {
4721       // create a ceiling edge
4722       if ( !isQuadratic ) {
4723         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4724                                vecNewNodes[ 1 ]->second.back())) {
4725           myLastCreatedElems.push_back(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4726                                                       vecNewNodes[ 1 ]->second.back()));
4727           srcElements.push_back( elem );
4728         }
4729       }
4730       else {
4731         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4732                                vecNewNodes[ 1 ]->second.back(),
4733                                vecNewNodes[ 2 ]->second.back())) {
4734           myLastCreatedElems.push_back(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4735                                                       vecNewNodes[ 1 ]->second.back(),
4736                                                       vecNewNodes[ 2 ]->second.back()));
4737           srcElements.push_back( elem );
4738         }
4739       }
4740     }
4741     if ( elem->GetType() != SMDSAbs_Face )
4742       continue;
4743
4744     bool hasFreeLinks = false;
4745
4746     TIDSortedElemSet avoidSet;
4747     avoidSet.insert( elem );
4748
4749     set<const SMDS_MeshNode*> aFaceLastNodes;
4750     int iNode, nbNodes = vecNewNodes.size();
4751     if ( !isQuadratic ) {
4752       // loop on the face nodes
4753       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4754         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4755         // look for free links of the face
4756         int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
4757         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4758         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4759         // check if a link n1-n2 is free
4760         if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
4761           hasFreeLinks = true;
4762           // make a new edge and a ceiling for a new edge
4763           const SMDS_MeshElement* edge;
4764           if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
4765             myLastCreatedElems.push_back( edge = aMesh->AddEdge( n1, n2 )); // free link edge
4766             srcElements.push_back( myLastCreatedElems.back() );
4767           }
4768           n1 = vecNewNodes[ iNode ]->second.back();
4769           n2 = vecNewNodes[ iNext ]->second.back();
4770           if ( !aMesh->FindEdge( n1, n2 )) {
4771             myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2 )); // new edge ceiling
4772             srcElements.push_back( edge );
4773           }
4774         }
4775       }
4776     }
4777     else { // elem is quadratic face
4778       int nbn = nbNodes/2;
4779       for ( iNode = 0; iNode < nbn; iNode++ ) {
4780         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4781         int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
4782         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4783         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4784         const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
4785         // check if a link is free
4786         if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
4787              ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
4788              ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
4789           hasFreeLinks = true;
4790           // make an edge and a ceiling for a new edge
4791           // find medium node
4792           if ( !aMesh->FindEdge( n1, n2, n3 )) {
4793             myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2, n3 )); // free link edge
4794             srcElements.push_back( elem );
4795           }
4796           n1 = vecNewNodes[ iNode ]->second.back();
4797           n2 = vecNewNodes[ iNext ]->second.back();
4798           n3 = vecNewNodes[ iNode+nbn ]->second.back();
4799           if ( !aMesh->FindEdge( n1, n2, n3 )) {
4800             myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
4801             srcElements.push_back( elem );
4802           }
4803         }
4804       }
4805       for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
4806         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4807       }
4808     }
4809
4810     // sweep free links into faces
4811
4812     if ( hasFreeLinks ) {
4813       list<const SMDS_MeshElement*> & newVolumes = itElem->second;
4814       int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
4815
4816       set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
4817       set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
4818       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4819         initNodeSet.insert( vecNewNodes[ iNode ]->first );
4820         topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
4821       }
4822       if ( isQuadratic && nbNodes % 2 ) {  // node set for the case of a biquadratic
4823         initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
4824         initNodeSetNoCenter.erase( vecNewNodes.back()->first );
4825       }
4826       for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
4827         list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
4828         std::advance( v, volNb );
4829         // find indices of free faces of a volume and their source edges
4830         list< int > freeInd;
4831         list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
4832         SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
4833         int iF, nbF = vTool.NbFaces();
4834         for ( iF = 0; iF < nbF; iF ++ ) {
4835           if ( vTool.IsFreeFace( iF ) &&
4836                vTool.GetFaceNodes( iF, faceNodeSet ) &&
4837                initNodeSet != faceNodeSet) // except an initial face
4838           {
4839             if ( nbSteps == 1 && faceNodeSet == topNodeSet )
4840               continue;
4841             if ( faceNodeSet == initNodeSetNoCenter )
4842               continue;
4843             freeInd.push_back( iF );
4844             // find source edge of a free face iF
4845             vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
4846             vector<const SMDS_MeshNode*>::iterator lastCommom;
4847             commonNodes.resize( nbNodes, 0 );
4848             lastCommom = std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
4849                                                 initNodeSet.begin(), initNodeSet.end(),
4850                                                 commonNodes.begin());
4851             if ( std::distance( commonNodes.begin(), lastCommom ) == 3 )
4852               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
4853             else
4854               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
4855 #ifdef _DEBUG_
4856             if ( !srcEdges.back() )
4857             {
4858               cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
4859                    << iF << " of volume #" << vTool.ID() << endl;
4860             }
4861 #endif
4862           }
4863         }
4864         if ( freeInd.empty() )
4865           continue;
4866
4867         // create wall faces for all steps;
4868         // if such a face has been already created by sweep of edge,
4869         // assure that its orientation is OK
4870         for ( int iStep = 0; iStep < nbSteps; iStep++ )
4871         {
4872           vTool.Set( *v, /*ignoreCentralNodes=*/false );
4873           vTool.SetExternalNormal();
4874           const int nextShift = vTool.IsForward() ? +1 : -1;
4875           list< int >::iterator ind = freeInd.begin();
4876           list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
4877           for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
4878           {
4879             const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
4880             int nbn = vTool.NbFaceNodes( *ind );
4881             const SMDS_MeshElement * f = 0;
4882             if ( nbn == 3 )              ///// triangle
4883             {
4884               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
4885               if ( !f ||
4886                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4887               {
4888                 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
4889                                                      nodes[ 1 ],
4890                                                      nodes[ 1 + nextShift ] };
4891                 if ( f )
4892                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4893                 else
4894                   myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4895                                                                newOrder[ 2 ] ));
4896               }
4897             }
4898             else if ( nbn == 4 )       ///// quadrangle
4899             {
4900               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
4901               if ( !f ||
4902                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4903               {
4904                 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
4905                                                      nodes[ 2 ], nodes[ 2+nextShift ] };
4906                 if ( f )
4907                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4908                 else
4909                   myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4910                                                                newOrder[ 2 ], newOrder[ 3 ]));
4911               }
4912             }
4913             else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
4914             {
4915               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
4916               if ( !f ||
4917                    nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
4918               {
4919                 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
4920                                                      nodes[2],
4921                                                      nodes[2 + 2*nextShift],
4922                                                      nodes[3 - 2*nextShift],
4923                                                      nodes[3],
4924                                                      nodes[3 + 2*nextShift]};
4925                 if ( f )
4926                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4927                 else
4928                   myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ],
4929                                                                newOrder[ 1 ],
4930                                                                newOrder[ 2 ],
4931                                                                newOrder[ 3 ],
4932                                                                newOrder[ 4 ],
4933                                                                newOrder[ 5 ] ));
4934               }
4935             }
4936             else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
4937             {
4938               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
4939                                    nodes[1], nodes[3], nodes[5], nodes[7] );
4940               if ( !f ||
4941                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4942               {
4943                 const SMDS_MeshNode* newOrder[8] = { nodes[0],
4944                                                      nodes[4 - 2*nextShift],
4945                                                      nodes[4],
4946                                                      nodes[4 + 2*nextShift],
4947                                                      nodes[1],
4948                                                      nodes[5 - 2*nextShift],
4949                                                      nodes[5],
4950                                                      nodes[5 + 2*nextShift] };
4951                 if ( f )
4952                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4953                 else
4954                   myLastCreatedElems.push_back(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4955                                                               newOrder[ 2 ], newOrder[ 3 ],
4956                                                               newOrder[ 4 ], newOrder[ 5 ],
4957                                                               newOrder[ 6 ], newOrder[ 7 ]));
4958               }
4959             }
4960             else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
4961             {
4962               f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
4963                                       SMDSAbs_Face, /*noMedium=*/false);
4964               if ( !f ||
4965                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4966               {
4967                 const SMDS_MeshNode* newOrder[9] = { nodes[0],
4968                                                      nodes[4 - 2*nextShift],
4969                                                      nodes[4],
4970                                                      nodes[4 + 2*nextShift],
4971                                                      nodes[1],
4972                                                      nodes[5 - 2*nextShift],
4973                                                      nodes[5],
4974                                                      nodes[5 + 2*nextShift],
4975                                                      nodes[8] };
4976                 if ( f )
4977                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4978                 else
4979                   myLastCreatedElems.push_back(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4980                                                               newOrder[ 2 ], newOrder[ 3 ],
4981                                                               newOrder[ 4 ], newOrder[ 5 ],
4982                                                               newOrder[ 6 ], newOrder[ 7 ],
4983                                                               newOrder[ 8 ]));
4984               }
4985             }
4986             else  //////// polygon
4987             {
4988               vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
4989               const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
4990               if ( !f ||
4991                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
4992               {
4993                 if ( !vTool.IsForward() )
4994                   std::reverse( polygon_nodes.begin(), polygon_nodes.end());
4995                 if ( f )
4996                   aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
4997                 else
4998                   AddElement( polygon_nodes, polyFace.SetQuad( (*v)->IsQuadratic() ));
4999               }
5000             }
5001
5002             while ( srcElements.size() < myLastCreatedElems.size() )
5003               srcElements.push_back( *srcEdge );
5004
5005           }  // loop on free faces
5006
5007           // go to the next volume
5008           iVol = 0;
5009           while ( iVol++ < nbVolumesByStep ) v++;
5010
5011         } // loop on steps
5012       } // loop on volumes of one step
5013     } // sweep free links into faces
5014
5015     // Make a ceiling face with a normal external to a volume
5016
5017     // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
5018     SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
5019     int iF = lastVol.GetFaceIndex( aFaceLastNodes );
5020
5021     if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
5022       aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
5023       iF = lastVol.GetFaceIndex( aFaceLastNodes );
5024     }
5025     if ( iF >= 0 )
5026     {
5027       lastVol.SetExternalNormal();
5028       const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
5029       const               int nbn = lastVol.NbFaceNodes( iF );
5030       vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
5031       if ( !hasFreeLinks ||
5032            !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
5033       {
5034         const vector<int>& interlace =
5035           SMDS_MeshCell::interlacedSmdsOrder( elem->GetEntityType(), nbn );
5036         SMDS_MeshCell::applyInterlaceRev( interlace, nodeVec );
5037
5038         AddElement( nodeVec, anyFace.Init( elem ));
5039
5040         while ( srcElements.size() < myLastCreatedElems.size() )
5041           srcElements.push_back( elem );
5042       }
5043     }
5044   } // loop on swept elements
5045 }
5046
5047 //=======================================================================
5048 //function : RotationSweep
5049 //purpose  :
5050 //=======================================================================
5051
5052 SMESH_MeshEditor::PGroupIDs
5053 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet   theElemSets[2],
5054                                 const gp_Ax1&      theAxis,
5055                                 const double       theAngle,
5056                                 const int          theNbSteps,
5057                                 const double       theTol,
5058                                 const bool         theMakeGroups,
5059                                 const bool         theMakeWalls)
5060 {
5061   ClearLastCreated();
5062
5063   setElemsFirst( theElemSets );
5064   myLastCreatedElems.reserve( theElemSets[0].size() * theNbSteps );
5065   myLastCreatedNodes.reserve( theElemSets[1].size() * theNbSteps );
5066
5067   // source elements for each generated one
5068   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5069   srcElems.reserve( theElemSets[0].size() );
5070   srcNodes.reserve( theElemSets[1].size() );
5071
5072   gp_Trsf aTrsf;
5073   aTrsf.SetRotation( theAxis, theAngle );
5074   gp_Trsf aTrsf2;
5075   aTrsf2.SetRotation( theAxis, theAngle/2. );
5076
5077   gp_Lin aLine( theAxis );
5078   double aSqTol = theTol * theTol;
5079
5080   SMESHDS_Mesh* aMesh = GetMeshDS();
5081
5082   TNodeOfNodeListMap mapNewNodes;
5083   TElemOfVecOfNnlmiMap mapElemNewNodes;
5084   TTElemOfElemListMap newElemsMap;
5085
5086   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5087                                      myMesh->NbFaces(ORDER_QUADRATIC) +
5088                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
5089   // loop on theElemSets
5090   TIDSortedElemSet::iterator itElem;
5091   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5092   {
5093     TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5094     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5095       const SMDS_MeshElement* elem = *itElem;
5096       if ( !elem || elem->GetType() == SMDSAbs_Volume )
5097         continue;
5098       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5099       newNodesItVec.reserve( elem->NbNodes() );
5100
5101       // loop on elem nodes
5102       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5103       while ( itN->more() )
5104       {
5105         const SMDS_MeshNode* node = cast2Node( itN->next() );
5106
5107         gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
5108         double coord[3];
5109         aXYZ.Coord( coord[0], coord[1], coord[2] );
5110         bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
5111
5112         // check if a node has been already sweeped
5113         TNodeOfNodeListMapItr nIt =
5114           mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5115         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5116         if ( listNewNodes.empty() )
5117         {
5118           // check if we are to create medium nodes between corner ones
5119           bool needMediumNodes = false;
5120           if ( isQuadraticMesh )
5121           {
5122             SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5123             while (it->more() && !needMediumNodes )
5124             {
5125               const SMDS_MeshElement* invElem = it->next();
5126               if ( invElem != elem && !theElems.count( invElem )) continue;
5127               needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5128               if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5129                 needMediumNodes = true;
5130             }
5131           }
5132
5133           // make new nodes
5134           const SMDS_MeshNode * newNode = node;
5135           for ( int i = 0; i < theNbSteps; i++ ) {
5136             if ( !isOnAxis ) {
5137               if ( needMediumNodes )  // create a medium node
5138               {
5139                 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5140                 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5141                 myLastCreatedNodes.push_back(newNode);
5142                 srcNodes.push_back( node );
5143                 listNewNodes.push_back( newNode );
5144                 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5145               }
5146               else {
5147                 aTrsf.Transforms( coord[0], coord[1], coord[2] );
5148               }
5149               // create a corner node
5150               newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5151               myLastCreatedNodes.push_back(newNode);
5152               srcNodes.push_back( node );
5153               listNewNodes.push_back( newNode );
5154             }
5155             else {
5156               listNewNodes.push_back( newNode );
5157               // if ( needMediumNodes )
5158               //   listNewNodes.push_back( newNode );
5159             }
5160           }
5161         }
5162         newNodesItVec.push_back( nIt );
5163       }
5164       // make new elements
5165       sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
5166     }
5167   }
5168
5169   if ( theMakeWalls )
5170     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], theNbSteps, srcElems );
5171
5172   PGroupIDs newGroupIDs;
5173   if ( theMakeGroups )
5174     newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
5175
5176   return newGroupIDs;
5177 }
5178
5179 //=======================================================================
5180 //function : ExtrusParam
5181 //purpose  : standard construction
5182 //=======================================================================
5183
5184 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Vec&            theStep,
5185                                             const int                theNbSteps,
5186                                             const std::list<double>& theScales,
5187                                             const gp_XYZ*            theBasePoint,
5188                                             const int                theFlags,
5189                                             const double             theTolerance):
5190   myDir( theStep ),
5191   myBaseP( Precision::Infinite(), 0, 0 ),
5192   myFlags( theFlags ),
5193   myTolerance( theTolerance ),
5194   myElemsToUse( NULL )
5195 {
5196   mySteps = new TColStd_HSequenceOfReal;
5197   const double stepSize = theStep.Magnitude();
5198   for (int i=1; i<=theNbSteps; i++ )
5199     mySteps->Append( stepSize );
5200
5201   int nbScales = theScales.size();
5202   if ( nbScales > 0 )
5203   {
5204     if ( IsLinearVariation() && nbScales < theNbSteps )
5205     {
5206       myScales.reserve( theNbSteps );
5207       std::list<double>::const_iterator scale = theScales.begin();
5208       double prevScale = 1.0;
5209       for ( int iSc = 1; scale != theScales.end(); ++scale, ++iSc )
5210       {
5211         int      iStep = int( iSc / double( nbScales ) * theNbSteps + 0.5 );
5212         int    stDelta = Max( 1, iStep - myScales.size());
5213         double scDelta = ( *scale - prevScale ) / stDelta;
5214         for ( int iStep = 0; iStep < stDelta; ++iStep )
5215         {
5216           myScales.push_back( prevScale + scDelta );
5217           prevScale = myScales.back();
5218         }
5219         prevScale = *scale;
5220       }
5221     }
5222     else
5223     {
5224       myScales.assign( theScales.begin(), theScales.end() );
5225     }
5226   }
5227   if ( theBasePoint )
5228   {
5229     myBaseP = *theBasePoint;
5230   }
5231
5232   if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5233       ( theTolerance > 0 ))
5234   {
5235     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5236   }
5237   else
5238   {
5239     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5240   }
5241 }
5242
5243 //=======================================================================
5244 //function : ExtrusParam
5245 //purpose  : steps are given explicitly
5246 //=======================================================================
5247
5248 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Dir&                   theDir,
5249                                             Handle(TColStd_HSequenceOfReal) theSteps,
5250                                             const int                       theFlags,
5251                                             const double                    theTolerance):
5252   myDir( theDir ),
5253   mySteps( theSteps ),
5254   myFlags( theFlags ),
5255   myTolerance( theTolerance ),
5256   myElemsToUse( NULL )
5257 {
5258   if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5259       ( theTolerance > 0 ))
5260   {
5261     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5262   }
5263   else
5264   {
5265     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5266   }
5267 }
5268
5269 //=======================================================================
5270 //function : ExtrusParam
5271 //purpose  : for extrusion by normal
5272 //=======================================================================
5273
5274 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const double theStepSize,
5275                                             const int    theNbSteps,
5276                                             const int    theFlags,
5277                                             const int    theDim ):
5278   myDir( 1,0,0 ),
5279   mySteps( new TColStd_HSequenceOfReal ),
5280   myFlags( theFlags ),
5281   myTolerance( 0 ),
5282   myElemsToUse( NULL )
5283 {
5284   for (int i = 0; i < theNbSteps; i++ )
5285     mySteps->Append( theStepSize );
5286
5287   if ( theDim == 1 )
5288   {
5289     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal1D;
5290   }
5291   else
5292   {
5293     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal2D;
5294   }
5295 }
5296
5297 //=======================================================================
5298 //function : ExtrusParam::SetElementsToUse
5299 //purpose  : stores elements to use for extrusion by normal, depending on
5300 //           state of EXTRUSION_FLAG_USE_INPUT_ELEMS_ONLY flag;
5301 //           define myBaseP for scaling
5302 //=======================================================================
5303
5304 void SMESH_MeshEditor::ExtrusParam::SetElementsToUse( const TIDSortedElemSet& elems,
5305                                                       const TIDSortedElemSet& nodes )
5306 {
5307   myElemsToUse = ToUseInpElemsOnly() ? & elems : 0;
5308
5309   if ( Precision::IsInfinite( myBaseP.X() )) // myBaseP not defined
5310   {
5311     myBaseP.SetCoord( 0.,0.,0. );
5312     TIDSortedElemSet newNodes;
5313
5314     const TIDSortedElemSet* elemSets[] = { &elems, &nodes };
5315     for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5316     {
5317       const TIDSortedElemSet& elements = *( elemSets[ is2ndSet ]);
5318       TIDSortedElemSet::const_iterator itElem = elements.begin();
5319       for ( ; itElem != elements.end(); itElem++ )
5320       {
5321         const SMDS_MeshElement* elem = *itElem;
5322         SMDS_ElemIteratorPtr     itN = elem->nodesIterator();
5323         while ( itN->more() ) {
5324           const SMDS_MeshElement* node = itN->next();
5325           if ( newNodes.insert( node ).second )
5326             myBaseP += SMESH_NodeXYZ( node );
5327         }
5328       }
5329     }
5330     myBaseP /= newNodes.size();
5331   }
5332 }
5333
5334 //=======================================================================
5335 //function : ExtrusParam::beginStepIter
5336 //purpose  : prepare iteration on steps
5337 //=======================================================================
5338
5339 void SMESH_MeshEditor::ExtrusParam::beginStepIter( bool withMediumNodes )
5340 {
5341   myWithMediumNodes = withMediumNodes;
5342   myNextStep = 1;
5343   myCurSteps.clear();
5344 }
5345 //=======================================================================
5346 //function : ExtrusParam::moreSteps
5347 //purpose  : are there more steps?
5348 //=======================================================================
5349
5350 bool SMESH_MeshEditor::ExtrusParam::moreSteps()
5351 {
5352   return myNextStep <= mySteps->Length() || !myCurSteps.empty();
5353 }
5354 //=======================================================================
5355 //function : ExtrusParam::nextStep
5356 //purpose  : returns the next step
5357 //=======================================================================
5358
5359 double SMESH_MeshEditor::ExtrusParam::nextStep()
5360 {
5361   double res = 0;
5362   if ( !myCurSteps.empty() )
5363   {
5364     res = myCurSteps.back();
5365     myCurSteps.pop_back();
5366   }
5367   else if ( myNextStep <= mySteps->Length() )
5368   {
5369     myCurSteps.push_back( mySteps->Value( myNextStep ));
5370     ++myNextStep;
5371     if ( myWithMediumNodes )
5372     {
5373       myCurSteps.back() /= 2.;
5374       myCurSteps.push_back( myCurSteps.back() );
5375     }
5376     res = nextStep();
5377   }
5378   return res;
5379 }
5380
5381 //=======================================================================
5382 //function : ExtrusParam::makeNodesByDir
5383 //purpose  : create nodes for standard extrusion
5384 //=======================================================================
5385
5386 int SMESH_MeshEditor::ExtrusParam::
5387 makeNodesByDir( SMESHDS_Mesh*                     mesh,
5388                 const SMDS_MeshNode*              srcNode,
5389                 std::list<const SMDS_MeshNode*> & newNodes,
5390                 const bool                        makeMediumNodes)
5391 {
5392   gp_XYZ p = SMESH_NodeXYZ( srcNode );
5393
5394   int nbNodes = 0;
5395   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5396   {
5397     p += myDir.XYZ() * nextStep();
5398     const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5399     newNodes.push_back( newNode );
5400   }
5401
5402   if ( !myScales.empty() )
5403   {
5404     if ( makeMediumNodes && myMediumScales.empty() )
5405     {
5406       myMediumScales.resize( myScales.size() );
5407       double prevFactor = 1.;
5408       for ( size_t i = 0; i < myScales.size(); ++i )
5409       {
5410         myMediumScales[i] = 0.5 * ( prevFactor + myScales[i] );
5411         prevFactor = myScales[i];
5412       }
5413     }
5414     typedef std::vector<double>::iterator ScaleIt;
5415     ScaleIt scales[] = { myScales.begin(), myMediumScales.begin() };
5416
5417     size_t iSc = 0, nbScales = myScales.size() + myMediumScales.size();
5418
5419     gp_XYZ center = myBaseP;
5420     std::list<const SMDS_MeshNode*>::iterator nIt = newNodes.begin();
5421     size_t iN  = 0;
5422     for ( beginStepIter( makeMediumNodes ); moreSteps() && ( iN < nbScales ); ++nIt, ++iN )
5423     {
5424       center += myDir.XYZ() * nextStep();
5425
5426       iSc += int( makeMediumNodes );
5427       ScaleIt& scale = scales[ iSc % 2 ];
5428       
5429       gp_XYZ xyz = SMESH_NodeXYZ( *nIt );
5430       xyz = ( *scale * ( xyz - center )) + center;
5431       mesh->MoveNode( *nIt, xyz.X(), xyz.Y(), xyz.Z() );
5432
5433       ++scale;
5434     }
5435   }
5436   return nbNodes;
5437 }
5438
5439 //=======================================================================
5440 //function : ExtrusParam::makeNodesByDirAndSew
5441 //purpose  : create nodes for standard extrusion with sewing
5442 //=======================================================================
5443
5444 int SMESH_MeshEditor::ExtrusParam::
5445 makeNodesByDirAndSew( SMESHDS_Mesh*                     mesh,
5446                       const SMDS_MeshNode*              srcNode,
5447                       std::list<const SMDS_MeshNode*> & newNodes,
5448                       const bool                        makeMediumNodes)
5449 {
5450   gp_XYZ P1 = SMESH_NodeXYZ( srcNode );
5451
5452   int nbNodes = 0;
5453   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5454   {
5455     P1 += myDir.XYZ() * nextStep();
5456
5457     // try to search in sequence of existing nodes
5458     // if myNodes.size()>0 we 'nave to use given sequence
5459     // else - use all nodes of mesh
5460     const SMDS_MeshNode * node = 0;
5461     if ( myNodes.Length() > 0 )
5462     {
5463       for ( int i = 1; i <= myNodes.Length(); i++ )
5464       {
5465         SMESH_NodeXYZ P2 = myNodes.Value(i);
5466         if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5467         {
5468           node = myNodes.Value(i);
5469           break;
5470         }
5471       }
5472     }
5473     else
5474     {
5475       SMDS_NodeIteratorPtr itn = mesh->nodesIterator();
5476       while(itn->more())
5477       {
5478         SMESH_NodeXYZ P2 = itn->next();
5479         if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5480         {
5481           node = P2._node;
5482           break;
5483         }
5484       }
5485     }
5486
5487     if ( !node )
5488       node = mesh->AddNode( P1.X(), P1.Y(), P1.Z() );
5489
5490     newNodes.push_back( node );
5491
5492   } // loop on steps
5493
5494   return nbNodes;
5495 }
5496
5497 //=======================================================================
5498 //function : ExtrusParam::makeNodesByNormal2D
5499 //purpose  : create nodes for extrusion using normals of faces
5500 //=======================================================================
5501
5502 int SMESH_MeshEditor::ExtrusParam::
5503 makeNodesByNormal2D( SMESHDS_Mesh*                     mesh,
5504                      const SMDS_MeshNode*              srcNode,
5505                      std::list<const SMDS_MeshNode*> & newNodes,
5506                      const bool                        makeMediumNodes)
5507 {
5508   const bool alongAvgNorm = ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL );
5509
5510   gp_XYZ p = SMESH_NodeXYZ( srcNode );
5511
5512   // get normals to faces sharing srcNode
5513   vector< gp_XYZ > norms, baryCenters;
5514   gp_XYZ norm, avgNorm( 0,0,0 );
5515   SMDS_ElemIteratorPtr faceIt = srcNode->GetInverseElementIterator( SMDSAbs_Face );
5516   while ( faceIt->more() )
5517   {
5518     const SMDS_MeshElement* face = faceIt->next();
5519     if ( myElemsToUse && !myElemsToUse->count( face ))
5520       continue;
5521     if ( SMESH_MeshAlgos::FaceNormal( face, norm, /*normalized=*/true ))
5522     {
5523       norms.push_back( norm );
5524       avgNorm += norm;
5525       if ( !alongAvgNorm )
5526       {
5527         gp_XYZ bc(0,0,0);
5528         int nbN = 0;
5529         for ( SMDS_ElemIteratorPtr nIt = face->nodesIterator(); nIt->more(); ++nbN )
5530           bc += SMESH_NodeXYZ( nIt->next() );
5531         baryCenters.push_back( bc / nbN );
5532       }
5533     }
5534   }
5535
5536   if ( norms.empty() ) return 0;
5537
5538   double normSize = avgNorm.Modulus();
5539   if ( normSize < std::numeric_limits<double>::min() )
5540     return 0;
5541
5542   if ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL ) // extrude along avgNorm
5543   {
5544     myDir = avgNorm;
5545     return makeNodesByDir( mesh, srcNode, newNodes, makeMediumNodes );
5546   }
5547
5548   avgNorm /= normSize;
5549
5550   int nbNodes = 0;
5551   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5552   {
5553     gp_XYZ pNew = p;
5554     double stepSize = nextStep();
5555
5556     if ( norms.size() > 1 )
5557     {
5558       for ( size_t iF = 0; iF < norms.size(); ++iF ) // loop on faces
5559       {
5560         // translate plane of a face
5561         baryCenters[ iF ] += norms[ iF ] * stepSize;
5562
5563         // find point of intersection of the face plane located at baryCenters[ iF ]
5564         // and avgNorm located at pNew
5565         double d    = -( norms[ iF ] * baryCenters[ iF ]); // d of plane equation ax+by+cz+d=0
5566         double dot  = ( norms[ iF ] * avgNorm );
5567         if ( dot < std::numeric_limits<double>::min() )
5568           dot = stepSize * 1e-3;
5569         double step = -( norms[ iF ] * pNew + d ) / dot;
5570         pNew += step * avgNorm;
5571       }
5572     }
5573     else
5574     {
5575       pNew += stepSize * avgNorm;
5576     }
5577     p = pNew;
5578
5579     const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5580     newNodes.push_back( newNode );
5581   }
5582   return nbNodes;
5583 }
5584
5585 //=======================================================================
5586 //function : ExtrusParam::makeNodesByNormal1D
5587 //purpose  : create nodes for extrusion using normals of edges
5588 //=======================================================================
5589
5590 int SMESH_MeshEditor::ExtrusParam::
5591 makeNodesByNormal1D( SMESHDS_Mesh*                     mesh,
5592                      const SMDS_MeshNode*              srcNode,
5593                      std::list<const SMDS_MeshNode*> & newNodes,
5594                      const bool                        makeMediumNodes)
5595 {
5596   throw SALOME_Exception("Extrusion 1D by Normal not implemented");
5597   return 0;
5598 }
5599
5600 //=======================================================================
5601 //function : ExtrusionSweep
5602 //purpose  :
5603 //=======================================================================
5604
5605 SMESH_MeshEditor::PGroupIDs
5606 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet     theElems[2],
5607                                   const gp_Vec&        theStep,
5608                                   const int            theNbSteps,
5609                                   TTElemOfElemListMap& newElemsMap,
5610                                   const int            theFlags,
5611                                   const double         theTolerance)
5612 {
5613   ExtrusParam aParams( theStep, theNbSteps, std::list<double>(), 0, theFlags, theTolerance );
5614   return ExtrusionSweep( theElems, aParams, newElemsMap );
5615 }
5616
5617
5618 //=======================================================================
5619 //function : ExtrusionSweep
5620 //purpose  :
5621 //=======================================================================
5622
5623 SMESH_MeshEditor::PGroupIDs
5624 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet     theElemSets[2],
5625                                   ExtrusParam&         theParams,
5626                                   TTElemOfElemListMap& newElemsMap)
5627 {
5628   ClearLastCreated();
5629
5630   setElemsFirst( theElemSets );
5631   myLastCreatedElems.reserve( theElemSets[0].size() * theParams.NbSteps() );
5632   myLastCreatedNodes.reserve( theElemSets[1].size() * theParams.NbSteps() );
5633
5634   // source elements for each generated one
5635   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5636   srcElems.reserve( theElemSets[0].size() );
5637   srcNodes.reserve( theElemSets[1].size() );
5638
5639   const int nbSteps = theParams.NbSteps();
5640   theParams.SetElementsToUse( theElemSets[0], theElemSets[1] );
5641
5642   TNodeOfNodeListMap   mapNewNodes;
5643   TElemOfVecOfNnlmiMap mapElemNewNodes;
5644
5645   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5646                                      myMesh->NbFaces(ORDER_QUADRATIC) +
5647                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
5648   // loop on theElems
5649   TIDSortedElemSet::iterator itElem;
5650   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5651   {
5652     TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5653     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5654     {
5655       // check element type
5656       const SMDS_MeshElement* elem = *itElem;
5657       if ( !elem  || elem->GetType() == SMDSAbs_Volume )
5658         continue;
5659
5660       const size_t nbNodes = elem->NbNodes();
5661       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5662       newNodesItVec.reserve( nbNodes );
5663
5664       // loop on elem nodes
5665       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5666       while ( itN->more() )
5667       {
5668         // check if a node has been already sweeped
5669         const SMDS_MeshNode* node = cast2Node( itN->next() );
5670         TNodeOfNodeListMap::iterator nIt =
5671           mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5672         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5673         if ( listNewNodes.empty() )
5674         {
5675           // make new nodes
5676
5677           // check if we are to create medium nodes between corner ones
5678           bool needMediumNodes = false;
5679           if ( isQuadraticMesh )
5680           {
5681             SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5682             while (it->more() && !needMediumNodes )
5683             {
5684               const SMDS_MeshElement* invElem = it->next();
5685               if ( invElem != elem && !theElems.count( invElem )) continue;
5686               needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5687               if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5688                 needMediumNodes = true;
5689             }
5690           }
5691           // create nodes for all steps
5692           if ( theParams.MakeNodes( GetMeshDS(), node, listNewNodes, needMediumNodes ))
5693           {
5694             list<const SMDS_MeshNode*>::iterator newNodesIt = listNewNodes.begin();
5695             for ( ; newNodesIt != listNewNodes.end(); ++newNodesIt )
5696             {
5697               myLastCreatedNodes.push_back( *newNodesIt );
5698               srcNodes.push_back( node );
5699             }
5700           }
5701           else
5702           {
5703             break; // newNodesItVec will be shorter than nbNodes
5704           }
5705         }
5706         newNodesItVec.push_back( nIt );
5707       }
5708       // make new elements
5709       if ( newNodesItVec.size() == nbNodes )
5710         sweepElement( elem, newNodesItVec, newElemsMap[elem], nbSteps, srcElems );
5711     }
5712   }
5713
5714   if ( theParams.ToMakeBoundary() ) {
5715     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], nbSteps, srcElems );
5716   }
5717   PGroupIDs newGroupIDs;
5718   if ( theParams.ToMakeGroups() )
5719     newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
5720
5721   return newGroupIDs;
5722 }
5723
5724 //=======================================================================
5725 //function : ExtrusionAlongTrack
5726 //purpose  :
5727 //=======================================================================
5728 SMESH_MeshEditor::Extrusion_Error
5729 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet     theElements[2],
5730                                        SMESH_subMesh*       theTrack,
5731                                        const SMDS_MeshNode* theN1,
5732                                        const bool           theHasAngles,
5733                                        list<double>&        theAngles,
5734                                        const bool           theLinearVariation,
5735                                        const bool           theHasRefPoint,
5736                                        const gp_Pnt&        theRefPoint,
5737                                        const bool           theMakeGroups)
5738 {
5739   ClearLastCreated();
5740
5741   int aNbE;
5742   std::list<double> aPrms;
5743   TIDSortedElemSet::iterator itElem;
5744
5745   gp_XYZ aGC;
5746   TopoDS_Edge aTrackEdge;
5747   TopoDS_Vertex aV1, aV2;
5748
5749   SMDS_ElemIteratorPtr aItE;
5750   SMDS_NodeIteratorPtr aItN;
5751   SMDSAbs_ElementType aTypeE;
5752
5753   TNodeOfNodeListMap mapNewNodes;
5754
5755   // 1. Check data
5756   aNbE = theElements[0].size() + theElements[1].size();
5757   // nothing to do
5758   if ( !aNbE )
5759     return EXTR_NO_ELEMENTS;
5760
5761   // 1.1 Track Pattern
5762   ASSERT( theTrack );
5763
5764   SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS();
5765   if ( !pSubMeshDS )
5766     return ExtrusionAlongTrack( theElements, theTrack->GetFather(), theN1,
5767                                 theHasAngles, theAngles, theLinearVariation,
5768                                 theHasRefPoint, theRefPoint, theMakeGroups );
5769
5770   aItE = pSubMeshDS->GetElements();
5771   while ( aItE->more() ) {
5772     const SMDS_MeshElement* pE = aItE->next();
5773     aTypeE = pE->GetType();
5774     // Pattern must contain links only
5775     if ( aTypeE != SMDSAbs_Edge )
5776       return EXTR_PATH_NOT_EDGE;
5777   }
5778
5779   list<SMESH_MeshEditor_PathPoint> fullList;
5780
5781   const TopoDS_Shape& aS = theTrack->GetSubShape();
5782   // Sub-shape for the Pattern must be an Edge or Wire
5783   if( aS.ShapeType() == TopAbs_EDGE ) {
5784     aTrackEdge = TopoDS::Edge( aS );
5785     // the Edge must not be degenerated
5786     if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
5787       return EXTR_BAD_PATH_SHAPE;
5788     TopExp::Vertices( aTrackEdge, aV1, aV2 );
5789     aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
5790     const SMDS_MeshNode* aN1 = aItN->next();
5791     aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
5792     const SMDS_MeshNode* aN2 = aItN->next();
5793     // starting node must be aN1 or aN2
5794     if ( !( aN1 == theN1 || aN2 == theN1 ) )
5795       return EXTR_BAD_STARTING_NODE;
5796     aItN = pSubMeshDS->GetNodes();
5797     while ( aItN->more() ) {
5798       const SMDS_MeshNode*  pNode = aItN->next();
5799       SMDS_EdgePositionPtr pEPos = pNode->GetPosition();
5800       double aT = pEPos->GetUParameter();
5801       aPrms.push_back( aT );
5802     }
5803     //Extrusion_Error err =
5804     makeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
5805   } else if( aS.ShapeType() == TopAbs_WIRE ) {
5806     list< SMESH_subMesh* > LSM;
5807     TopTools_SequenceOfShape Edges;
5808     SMESH_subMeshIteratorPtr itSM = theTrack->getDependsOnIterator(false,true);
5809     while(itSM->more()) {
5810       SMESH_subMesh* SM = itSM->next();
5811       LSM.push_back(SM);
5812       const TopoDS_Shape& aS = SM->GetSubShape();
5813       Edges.Append(aS);
5814     }
5815     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5816     int startNid = theN1->GetID();
5817     TColStd_MapOfInteger UsedNums;
5818
5819     int NbEdges = Edges.Length();
5820     int i = 1;
5821     for(; i<=NbEdges; i++) {
5822       int k = 0;
5823       list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
5824       for(; itLSM!=LSM.end(); itLSM++) {
5825         k++;
5826         if(UsedNums.Contains(k)) continue;
5827         aTrackEdge = TopoDS::Edge( Edges.Value(k) );
5828         SMESH_subMesh* locTrack = *itLSM;
5829         SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
5830         TopExp::Vertices( aTrackEdge, aV1, aV2 );
5831         aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
5832         const SMDS_MeshNode* aN1 = aItN->next();
5833         aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
5834         const SMDS_MeshNode* aN2 = aItN->next();
5835         // starting node must be aN1 or aN2
5836         if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
5837         // 2. Collect parameters on the track edge
5838         aPrms.clear();
5839         aItN = locMeshDS->GetNodes();
5840         while ( aItN->more() ) {
5841           const SMDS_MeshNode* pNode = aItN->next();
5842           SMDS_EdgePositionPtr pEPos = pNode->GetPosition();
5843           double aT = pEPos->GetUParameter();
5844           aPrms.push_back( aT );
5845         }
5846         list<SMESH_MeshEditor_PathPoint> LPP;
5847         //Extrusion_Error err =
5848         makeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
5849         LLPPs.push_back(LPP);
5850         UsedNums.Add(k);
5851         // update startN for search following edge
5852         if( aN1->GetID() == startNid ) startNid = aN2->GetID();
5853         else startNid = aN1->GetID();
5854         break;
5855       }
5856     }
5857     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5858     list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5859     list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5860     for(; itPP!=firstList.end(); itPP++) {
5861       fullList.push_back( *itPP );
5862     }
5863     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5864     fullList.pop_back();
5865     itLLPP++;
5866     for(; itLLPP!=LLPPs.end(); itLLPP++) {
5867       list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5868       itPP = currList.begin();
5869       SMESH_MeshEditor_PathPoint PP2 = currList.front();
5870       gp_Dir D1 = PP1.Tangent();
5871       gp_Dir D2 = PP2.Tangent();
5872       gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
5873                            (D1.Z()+D2.Z())/2 ) );
5874       PP1.SetTangent(Dnew);
5875       fullList.push_back(PP1);
5876       itPP++;
5877       for(; itPP!=firstList.end(); itPP++) {
5878         fullList.push_back( *itPP );
5879       }
5880       PP1 = fullList.back();
5881       fullList.pop_back();
5882     }
5883     // if wire not closed
5884     fullList.push_back(PP1);
5885     // else ???
5886   }
5887   else {
5888     return EXTR_BAD_PATH_SHAPE;
5889   }
5890
5891   return makeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
5892                           theHasRefPoint, theRefPoint, theMakeGroups);
5893 }
5894
5895
5896 //=======================================================================
5897 //function : ExtrusionAlongTrack
5898 //purpose  :
5899 //=======================================================================
5900 SMESH_MeshEditor::Extrusion_Error
5901 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet     theElements[2],
5902                                        SMESH_Mesh*          theTrack,
5903                                        const SMDS_MeshNode* theN1,
5904                                        const bool           theHasAngles,
5905                                        list<double>&        theAngles,
5906                                        const bool           theLinearVariation,
5907                                        const bool           theHasRefPoint,
5908                                        const gp_Pnt&        theRefPoint,
5909                                        const bool           theMakeGroups)
5910 {
5911   ClearLastCreated();
5912
5913   int aNbE;
5914   std::list<double> aPrms;
5915   TIDSortedElemSet::iterator itElem;
5916
5917   gp_XYZ aGC;
5918   TopoDS_Edge aTrackEdge;
5919   TopoDS_Vertex aV1, aV2;
5920
5921   SMDS_ElemIteratorPtr aItE;
5922   SMDS_NodeIteratorPtr aItN;
5923   SMDSAbs_ElementType aTypeE;
5924
5925   TNodeOfNodeListMap mapNewNodes;
5926
5927   // 1. Check data
5928   aNbE = theElements[0].size() + theElements[1].size();
5929   // nothing to do
5930   if ( !aNbE )
5931     return EXTR_NO_ELEMENTS;
5932
5933   // 1.1 Track Pattern
5934   ASSERT( theTrack );
5935
5936   SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS();
5937
5938   aItE = pMeshDS->elementsIterator();
5939   while ( aItE->more() ) {
5940     const SMDS_MeshElement* pE = aItE->next();
5941     aTypeE = pE->GetType();
5942     // Pattern must contain links only
5943     if ( aTypeE != SMDSAbs_Edge )
5944       return EXTR_PATH_NOT_EDGE;
5945   }
5946
5947   list<SMESH_MeshEditor_PathPoint> fullList;
5948
5949   const TopoDS_Shape& aS = theTrack->GetShapeToMesh();
5950
5951   if ( !theTrack->HasShapeToMesh() ) {
5952     //Mesh without shape
5953     const SMDS_MeshNode* currentNode = NULL;
5954     const SMDS_MeshNode* prevNode = theN1;
5955     std::vector<const SMDS_MeshNode*> aNodesList;
5956     aNodesList.push_back(theN1);
5957     int nbEdges = 0, conn=0;
5958     const SMDS_MeshElement* prevElem = NULL;
5959     const SMDS_MeshElement* currentElem = NULL;
5960     int totalNbEdges = theTrack->NbEdges();
5961     SMDS_ElemIteratorPtr nIt;
5962
5963     //check start node
5964     if( !theTrack->GetMeshDS()->Contains( theN1 )) {
5965       return EXTR_BAD_STARTING_NODE;
5966     }
5967
5968     conn = nbEdgeConnectivity(theN1);
5969     if( conn != 1 )
5970       return EXTR_PATH_NOT_EDGE;
5971
5972     aItE = theN1->GetInverseElementIterator();
5973     prevElem = aItE->next();
5974     currentElem = prevElem;
5975     //Get all nodes
5976     if(totalNbEdges == 1 ) {
5977       nIt = currentElem->nodesIterator();
5978       currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5979       if(currentNode == prevNode)
5980         currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5981       aNodesList.push_back(currentNode);
5982     } else {
5983       nIt = currentElem->nodesIterator();
5984       while( nIt->more() ) {
5985         currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5986         if(currentNode == prevNode)
5987           currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5988         aNodesList.push_back(currentNode);
5989
5990         //case of the closed mesh
5991         if(currentNode == theN1) {
5992           nbEdges++;
5993           break;
5994         }
5995
5996         conn = nbEdgeConnectivity(currentNode);
5997         if(conn > 2) {
5998           return EXTR_PATH_NOT_EDGE;
5999         }else if( conn == 1 && nbEdges > 0 ) {
6000           //End of the path
6001           nbEdges++;
6002           break;
6003         }else {
6004           prevNode = currentNode;
6005           aItE = currentNode->GetInverseElementIterator();
6006           currentElem = aItE->next();
6007           if( currentElem  == prevElem)
6008             currentElem = aItE->next();
6009           nIt = currentElem->nodesIterator();
6010           prevElem = currentElem;
6011           nbEdges++;
6012         }
6013       }
6014     }
6015
6016     if(nbEdges != totalNbEdges)
6017       return EXTR_PATH_NOT_EDGE;
6018
6019     TopTools_SequenceOfShape Edges;
6020     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6021     int startNid = theN1->GetID();
6022     for ( size_t i = 1; i < aNodesList.size(); i++ )
6023     {
6024       gp_Pnt     p1 = SMESH_NodeXYZ( aNodesList[i-1] );
6025       gp_Pnt     p2 = SMESH_NodeXYZ( aNodesList[i] );
6026       TopoDS_Edge e = BRepBuilderAPI_MakeEdge( p1, p2 );
6027       list<SMESH_MeshEditor_PathPoint> LPP;
6028       aPrms.clear();
6029       makeEdgePathPoints(aPrms, e, (aNodesList[i-1]->GetID()==startNid), LPP);
6030       LLPPs.push_back(LPP);
6031       if ( aNodesList[i-1]->GetID() == startNid ) startNid = aNodesList[i  ]->GetID();
6032       else                                        startNid = aNodesList[i-1]->GetID();
6033     }
6034
6035     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6036     list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
6037     list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
6038     for(; itPP!=firstList.end(); itPP++) {
6039       fullList.push_back( *itPP );
6040     }
6041
6042     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6043     SMESH_MeshEditor_PathPoint PP2;
6044     fullList.pop_back();
6045     itLLPP++;
6046     for(; itLLPP!=LLPPs.end(); itLLPP++) {
6047       list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
6048       itPP = currList.begin();
6049       PP2 = currList.front();
6050       gp_Dir D1 = PP1.Tangent();
6051       gp_Dir D2 = PP2.Tangent();
6052       gp_Dir Dnew( 0.5 * ( D1.XYZ() + D2.XYZ() ));
6053       PP1.SetTangent(Dnew);
6054       fullList.push_back(PP1);
6055       itPP++;
6056       for(; itPP!=currList.end(); itPP++) {
6057         fullList.push_back( *itPP );
6058       }
6059       PP1 = fullList.back();
6060       fullList.pop_back();
6061     }
6062     fullList.push_back(PP1);
6063
6064   } // Sub-shape for the Pattern must be an Edge or Wire
6065   else if ( aS.ShapeType() == TopAbs_EDGE )
6066   {
6067     aTrackEdge = TopoDS::Edge( aS );
6068     // the Edge must not be degenerated
6069     if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
6070       return EXTR_BAD_PATH_SHAPE;
6071     TopExp::Vertices( aTrackEdge, aV1, aV2 );
6072     const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6073     const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6074     // starting node must be aN1 or aN2
6075     if ( !( aN1 == theN1 || aN2 == theN1 ) )
6076       return EXTR_BAD_STARTING_NODE;
6077     aItN = pMeshDS->nodesIterator();
6078     while ( aItN->more() ) {
6079       const SMDS_MeshNode* pNode = aItN->next();
6080       if( pNode==aN1 || pNode==aN2 ) continue;
6081       SMDS_EdgePositionPtr pEPos = pNode->GetPosition();
6082       double aT = pEPos->GetUParameter();
6083       aPrms.push_back( aT );
6084     }
6085     //Extrusion_Error err =
6086     makeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
6087   }
6088   else if( aS.ShapeType() == TopAbs_WIRE ) {
6089     list< SMESH_subMesh* > LSM;
6090     TopTools_SequenceOfShape Edges;
6091     TopExp_Explorer eExp(aS, TopAbs_EDGE);
6092     for(; eExp.More(); eExp.Next()) {
6093       TopoDS_Edge E = TopoDS::Edge( eExp.Current() );
6094       if( SMESH_Algo::isDegenerated(E) ) continue;
6095       SMESH_subMesh* SM = theTrack->GetSubMesh(E);
6096       if(SM) {
6097         LSM.push_back(SM);
6098         Edges.Append(E);
6099       }
6100     }
6101     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6102     TopoDS_Vertex aVprev;
6103     TColStd_MapOfInteger UsedNums;
6104     int NbEdges = Edges.Length();
6105     int i = 1;
6106     for(; i<=NbEdges; i++) {
6107       int k = 0;
6108       list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
6109       for(; itLSM!=LSM.end(); itLSM++) {
6110         k++;
6111         if(UsedNums.Contains(k)) continue;
6112         aTrackEdge = TopoDS::Edge( Edges.Value(k) );
6113         SMESH_subMesh* locTrack = *itLSM;
6114         SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
6115         TopExp::Vertices( aTrackEdge, aV1, aV2 );
6116         bool aN1isOK = false, aN2isOK = false;
6117         if ( aVprev.IsNull() ) {
6118           // if previous vertex is not yet defined, it means that we in the beginning of wire
6119           // and we have to find initial vertex corresponding to starting node theN1
6120           const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6121           const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6122           // starting node must be aN1 or aN2
6123           aN1isOK = ( aN1 && aN1 == theN1 );
6124           aN2isOK = ( aN2 && aN2 == theN1 );
6125         }
6126         else {
6127           // we have specified ending vertex of the previous edge on the previous iteration
6128           // and we have just to check that it corresponds to any vertex in current segment
6129           aN1isOK = aVprev.IsSame( aV1 );
6130           aN2isOK = aVprev.IsSame( aV2 );
6131         }
6132         if ( !aN1isOK && !aN2isOK ) continue;
6133         // 2. Collect parameters on the track edge
6134         aPrms.clear();
6135         aItN = locMeshDS->GetNodes();
6136         while ( aItN->more() ) {
6137           const SMDS_MeshNode*  pNode = aItN->next();
6138           SMDS_EdgePositionPtr pEPos = pNode->GetPosition();
6139           double aT = pEPos->GetUParameter();
6140           aPrms.push_back( aT );
6141         }
6142         list<SMESH_MeshEditor_PathPoint> LPP;
6143         //Extrusion_Error err =
6144         makeEdgePathPoints(aPrms, aTrackEdge, aN1isOK, LPP);
6145         LLPPs.push_back(LPP);
6146         UsedNums.Add(k);
6147         // update startN for search following edge
6148         if ( aN1isOK ) aVprev = aV2;
6149         else           aVprev = aV1;
6150         break;
6151       }
6152     }
6153     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6154     list<SMESH_MeshEditor_PathPoint>& firstList = *itLLPP;
6155     fullList.splice( fullList.end(), firstList );
6156
6157     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6158     fullList.pop_back();
6159     itLLPP++;
6160     for(; itLLPP!=LLPPs.end(); itLLPP++) {
6161       list<SMESH_MeshEditor_PathPoint>& currList = *itLLPP;
6162       SMESH_MeshEditor_PathPoint PP2 = currList.front();
6163       gp_Dir D1 = PP1.Tangent();
6164       gp_Dir D2 = PP2.Tangent();
6165       gp_Dir Dnew( D1.XYZ() + D2.XYZ() );
6166       PP1.SetTangent(Dnew);
6167       fullList.push_back(PP1);
6168       fullList.splice( fullList.end(), currList, ++currList.begin(), currList.end() );
6169       PP1 = fullList.back();
6170       fullList.pop_back();
6171     }
6172     // if wire not closed
6173     fullList.push_back(PP1);
6174     // else ???
6175   }
6176   else {
6177     return EXTR_BAD_PATH_SHAPE;
6178   }
6179
6180   return makeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
6181                           theHasRefPoint, theRefPoint, theMakeGroups);
6182 }
6183
6184
6185 //=======================================================================
6186 //function : makeEdgePathPoints
6187 //purpose  : auxiliary for ExtrusionAlongTrack
6188 //=======================================================================
6189 SMESH_MeshEditor::Extrusion_Error
6190 SMESH_MeshEditor::makeEdgePathPoints(std::list<double>&                aPrms,
6191                                      const TopoDS_Edge&                aTrackEdge,
6192                                      bool                              FirstIsStart,
6193                                      list<SMESH_MeshEditor_PathPoint>& LPP)
6194 {
6195   Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2;
6196   aTolVec=1.e-7;
6197   aTolVec2=aTolVec*aTolVec;
6198   double aT1, aT2;
6199   TopoDS_Vertex aV1, aV2;
6200   TopExp::Vertices( aTrackEdge, aV1, aV2 );
6201   aT1=BRep_Tool::Parameter( aV1, aTrackEdge );
6202   aT2=BRep_Tool::Parameter( aV2, aTrackEdge );
6203   // 2. Collect parameters on the track edge
6204   aPrms.push_front( aT1 );
6205   aPrms.push_back( aT2 );
6206   // sort parameters
6207   aPrms.sort();
6208   if( FirstIsStart ) {
6209     if ( aT1 > aT2 ) {
6210       aPrms.reverse();
6211     }
6212   }
6213   else {
6214     if ( aT2 > aT1 ) {
6215       aPrms.reverse();
6216     }
6217   }
6218   // 3. Path Points
6219   SMESH_MeshEditor_PathPoint aPP;
6220   Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 );
6221   std::list<double>::iterator aItD = aPrms.begin();
6222   for(; aItD != aPrms.end(); ++aItD) {
6223     double aT = *aItD;
6224     gp_Pnt aP3D;
6225     gp_Vec aVec;
6226     aC3D->D1( aT, aP3D, aVec );
6227     aL2 = aVec.SquareMagnitude();
6228     if ( aL2 < aTolVec2 )
6229       return EXTR_CANT_GET_TANGENT;
6230     gp_Dir aTgt( FirstIsStart ? aVec : -aVec );
6231     aPP.SetPnt( aP3D );
6232     aPP.SetTangent( aTgt );
6233     aPP.SetParameter( aT );
6234     LPP.push_back(aPP);
6235   }
6236   return EXTR_OK;
6237 }
6238
6239
6240 //=======================================================================
6241 //function : makeExtrElements
6242 //purpose  : auxiliary for ExtrusionAlongTrack
6243 //=======================================================================
6244 SMESH_MeshEditor::Extrusion_Error
6245 SMESH_MeshEditor::makeExtrElements(TIDSortedElemSet                  theElemSets[2],
6246                                    list<SMESH_MeshEditor_PathPoint>& fullList,
6247                                    const bool                        theHasAngles,
6248                                    list<double>&                     theAngles,
6249                                    const bool                        theLinearVariation,
6250                                    const bool                        theHasRefPoint,
6251                                    const gp_Pnt&                     theRefPoint,
6252                                    const bool                        theMakeGroups)
6253 {
6254   const int aNbTP = fullList.size();
6255
6256   // Angles
6257   if( theHasAngles && !theAngles.empty() && theLinearVariation )
6258     linearAngleVariation(aNbTP-1, theAngles);
6259
6260   // fill vector of path points with angles
6261   vector<SMESH_MeshEditor_PathPoint> aPPs;
6262   list<SMESH_MeshEditor_PathPoint>::iterator itPP = fullList.begin();
6263   list<double>::iterator                 itAngles = theAngles.begin();
6264   aPPs.push_back( *itPP++ );
6265   for( ; itPP != fullList.end(); itPP++) {
6266     aPPs.push_back( *itPP );
6267     if ( theHasAngles && itAngles != theAngles.end() )
6268       aPPs.back().SetAngle( *itAngles++ );
6269   }
6270
6271   TNodeOfNodeListMap   mapNewNodes;
6272   TElemOfVecOfNnlmiMap mapElemNewNodes;
6273   TTElemOfElemListMap  newElemsMap;
6274   TIDSortedElemSet::iterator itElem;
6275   // source elements for each generated one
6276   SMESH_SequenceOfElemPtr srcElems, srcNodes;
6277
6278   // 3. Center of rotation aV0
6279   gp_Pnt aV0 = theRefPoint;
6280   if ( !theHasRefPoint )
6281   {
6282     gp_XYZ aGC( 0.,0.,0. );
6283     TIDSortedElemSet newNodes;
6284
6285     for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6286     {
6287       TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6288       itElem = theElements.begin();
6289       for ( ; itElem != theElements.end(); itElem++ )
6290       {
6291         const SMDS_MeshElement* elem = *itElem;
6292         SMDS_ElemIteratorPtr     itN = elem->nodesIterator();
6293         while ( itN->more() ) {
6294           const SMDS_MeshElement* node = itN->next();
6295           if ( newNodes.insert( node ).second )
6296             aGC += SMESH_NodeXYZ( node );
6297         }
6298       }
6299     }
6300     aGC /= newNodes.size();
6301     aV0.SetXYZ( aGC );
6302   } // if (!theHasRefPoint) {
6303
6304   // 4. Processing the elements
6305   SMESHDS_Mesh* aMesh = GetMeshDS();
6306   list<const SMDS_MeshNode*> emptyList;
6307
6308   setElemsFirst( theElemSets );
6309   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6310   {
6311     TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6312     for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ )
6313     {
6314       const SMDS_MeshElement* elem = *itElem;
6315
6316       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
6317       newNodesItVec.reserve( elem->NbNodes() );
6318
6319       // loop on elem nodes
6320       int nodeIndex = -1;
6321       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6322       while ( itN->more() )
6323       {
6324         ++nodeIndex;
6325         // check if a node has been already processed
6326         const SMDS_MeshNode* node = cast2Node( itN->next() );
6327         TNodeOfNodeListMap::iterator nIt = mapNewNodes.insert( make_pair( node, emptyList )).first;
6328         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
6329         if ( listNewNodes.empty() )
6330         {
6331           // make new nodes
6332           Standard_Real aAngle1x, aAngleT1T0, aTolAng;
6333           gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
6334           gp_Ax1 anAx1, anAxT1T0;
6335           gp_Dir aDT1x, aDT0x, aDT1T0;
6336
6337           aTolAng=1.e-4;
6338
6339           aV0x = aV0;
6340           aPN0 = SMESH_NodeXYZ( node );
6341
6342           const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
6343           aP0x = aPP0.Pnt();
6344           aDT0x= aPP0.Tangent();
6345
6346           for ( int j = 1; j < aNbTP; ++j ) {
6347             const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
6348             aP1x     = aPP1.Pnt();
6349             aDT1x    = aPP1.Tangent();
6350             aAngle1x = aPP1.Angle();
6351
6352             gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
6353             // Translation
6354             gp_Vec aV01x( aP0x, aP1x );
6355             aTrsf.SetTranslation( aV01x );
6356
6357             // traslated point
6358             aV1x = aV0x.Transformed( aTrsf );
6359             aPN1 = aPN0.Transformed( aTrsf );
6360
6361             // rotation 1 [ T1,T0 ]
6362             aAngleT1T0=-aDT1x.Angle( aDT0x );
6363             if (fabs(aAngleT1T0) > aTolAng)
6364             {
6365               aDT1T0=aDT1x^aDT0x;
6366               anAxT1T0.SetLocation( aV1x );
6367               anAxT1T0.SetDirection( aDT1T0 );
6368               aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
6369
6370               aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
6371             }
6372
6373             // rotation 2
6374             if ( theHasAngles ) {
6375               anAx1.SetLocation( aV1x );
6376               anAx1.SetDirection( aDT1x );
6377               aTrsfRot.SetRotation( anAx1, aAngle1x );
6378
6379               aPN1 = aPN1.Transformed( aTrsfRot );
6380             }
6381
6382             // make new node
6383             if ( elem->IsQuadratic() && !elem->IsMediumNode(node) )
6384             {
6385               // create additional node
6386               gp_XYZ midP = 0.5 * ( aPN1.XYZ() + aPN0.XYZ() );
6387               const SMDS_MeshNode* newNode = aMesh->AddNode( midP.X(), midP.Y(), midP.Z() );
6388               myLastCreatedNodes.push_back(newNode);
6389               srcNodes.push_back( node );
6390               listNewNodes.push_back( newNode );
6391             }
6392             const SMDS_MeshNode* newNode = aMesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
6393             myLastCreatedNodes.push_back(newNode);
6394             srcNodes.push_back( node );
6395             listNewNodes.push_back( newNode );
6396
6397             aPN0 = aPN1;
6398             aP0x = aP1x;
6399             aV0x = aV1x;
6400             aDT0x = aDT1x;
6401           }
6402         }
6403         else if( elem->IsQuadratic() && !elem->IsMediumNode(node) )
6404         {
6405           // if current elem is quadratic and current node is not medium
6406           // we have to check - may be it is needed to insert additional nodes
6407           list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
6408           if ((int) listNewNodes.size() == aNbTP-1 )
6409           {
6410             vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
6411             gp_XYZ P(node->X(), node->Y(), node->Z());
6412             list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
6413             int i;
6414             for(i=0; i<aNbTP-1; i++) {
6415               const SMDS_MeshNode* N = *it;
6416               double x = ( N->X() + P.X() )/2.;
6417               double y = ( N->Y() + P.Y() )/2.;
6418               double z = ( N->Z() + P.Z() )/2.;
6419               const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
6420               srcNodes.push_back( node );
6421               myLastCreatedNodes.push_back(newN);
6422               aNodes[2*i] = newN;
6423               aNodes[2*i+1] = N;
6424               P = gp_XYZ(N->X(),N->Y(),N->Z());
6425             }
6426             listNewNodes.clear();
6427             for(i=0; i<2*(aNbTP-1); i++) {
6428               listNewNodes.push_back(aNodes[i]);
6429             }
6430           }
6431         }
6432
6433         newNodesItVec.push_back( nIt );
6434       }
6435
6436       // make new elements
6437       sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
6438     }
6439   }
6440
6441   makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], aNbTP-1, srcElems );
6442
6443   if ( theMakeGroups )
6444     generateGroups( srcNodes, srcElems, "extruded");
6445
6446   return EXTR_OK;
6447 }
6448
6449
6450 //=======================================================================
6451 //function : linearAngleVariation
6452 //purpose  : spread values over nbSteps
6453 //=======================================================================
6454
6455 void SMESH_MeshEditor::linearAngleVariation(const int     nbSteps,
6456                                             list<double>& Angles)
6457 {
6458   int nbAngles = Angles.size();
6459   if( nbSteps > nbAngles && nbAngles > 0 )
6460   {
6461     vector<double> theAngles(nbAngles);
6462     theAngles.assign( Angles.begin(), Angles.end() );
6463
6464     list<double> res;
6465     double rAn2St = double( nbAngles ) / double( nbSteps );
6466     double angPrev = 0, angle;
6467     for ( int iSt = 0; iSt < nbSteps; ++iSt )
6468     {
6469       double angCur = rAn2St * ( iSt+1 );
6470       double angCurFloor  = floor( angCur );
6471       double angPrevFloor = floor( angPrev );
6472       if ( angPrevFloor == angCurFloor )
6473         angle = rAn2St * theAngles[ int( angCurFloor ) ];
6474       else {
6475         int iP = int( angPrevFloor );
6476         double angPrevCeil = ceil(angPrev);
6477         angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6478
6479         int iC = int( angCurFloor );
6480         if ( iC < nbAngles )
6481           angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6482
6483         iP = int( angPrevCeil );
6484         while ( iC-- > iP )
6485           angle += theAngles[ iC ];
6486       }
6487       res.push_back(angle);
6488       angPrev = angCur;
6489     }
6490     Angles.swap( res );
6491   }
6492 }
6493
6494
6495 //================================================================================
6496 /*!
6497  * \brief Move or copy theElements applying theTrsf to their nodes
6498  *  \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6499  *  \param theTrsf - transformation to apply
6500  *  \param theCopy - if true, create translated copies of theElems
6501  *  \param theMakeGroups - if true and theCopy, create translated groups
6502  *  \param theTargetMesh - mesh to copy translated elements into
6503  *  \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6504  */
6505 //================================================================================
6506
6507 SMESH_MeshEditor::PGroupIDs
6508 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6509                              const gp_Trsf&     theTrsf,
6510                              const bool         theCopy,
6511                              const bool         theMakeGroups,
6512                              SMESH_Mesh*        theTargetMesh)
6513 {
6514   ClearLastCreated();
6515   myLastCreatedElems.reserve( theElems.size() );
6516
6517   bool needReverse = false;
6518   string groupPostfix;
6519   switch ( theTrsf.Form() ) {
6520   case gp_PntMirror:
6521     needReverse = true;
6522     groupPostfix = "mirrored";
6523     break;
6524   case gp_Ax1Mirror:
6525     groupPostfix = "mirrored";
6526     break;
6527   case gp_Ax2Mirror:
6528     needReverse = true;
6529     groupPostfix = "mirrored";
6530     break;
6531   case gp_Rotation:
6532     groupPostfix = "rotated";
6533     break;
6534   case gp_Translation:
6535     groupPostfix = "translated";
6536     break;
6537   case gp_Scale:
6538     groupPostfix = "scaled";
6539     break;
6540   case gp_CompoundTrsf: // different scale by axis
6541     groupPostfix = "scaled";
6542     break;
6543   default:
6544     needReverse = false;
6545     groupPostfix = "transformed";
6546   }
6547
6548   SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6549   SMESHDS_Mesh* aMesh    = GetMeshDS();
6550
6551   SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6552   SMESH_MeshEditor* editor = theTargetMesh ? & targetMeshEditor : theCopy ? this : 0;
6553   SMESH_MeshEditor::ElemFeatures elemType;
6554
6555   // map old node to new one
6556   TNodeNodeMap nodeMap;
6557
6558   // elements sharing moved nodes; those of them which have all
6559   // nodes mirrored but are not in theElems are to be reversed
6560   TIDSortedElemSet inverseElemSet;
6561
6562   // source elements for each generated one
6563   SMESH_SequenceOfElemPtr srcElems, srcNodes;
6564
6565   // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6566   TIDSortedElemSet orphanNode;
6567
6568   if ( theElems.empty() ) // transform the whole mesh
6569   {
6570     // add all elements
6571     SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6572     while ( eIt->more() ) theElems.insert( eIt->next() );
6573     // add orphan nodes
6574     SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6575     while ( nIt->more() )
6576     {
6577       const SMDS_MeshNode* node = nIt->next();
6578       if ( node->NbInverseElements() == 0)
6579         orphanNode.insert( node );
6580     }
6581   }
6582
6583   // loop on elements to transform nodes : first orphan nodes then elems
6584   TIDSortedElemSet::iterator itElem;
6585   TIDSortedElemSet *elements[] = { &orphanNode, &theElems };
6586   for (int i=0; i<2; i++)
6587     for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ )
6588     {
6589       const SMDS_MeshElement* elem = *itElem;
6590       if ( !elem )
6591         continue;
6592
6593       // loop on elem nodes
6594       double coord[3];
6595       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6596       while ( itN->more() )
6597       {
6598         const SMDS_MeshNode* node = cast2Node( itN->next() );
6599         // check if a node has been already transformed
6600         pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6601           nodeMap.insert( make_pair ( node, node ));
6602         if ( !n2n_isnew.second )
6603           continue;
6604
6605         node->GetXYZ( coord );
6606         theTrsf.Transforms( coord[0], coord[1], coord[2] );
6607         if ( theTargetMesh ) {
6608           const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6609           n2n_isnew.first->second = newNode;
6610           myLastCreatedNodes.push_back(newNode);
6611           srcNodes.push_back( node );
6612         }
6613         else if ( theCopy ) {
6614           const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6615           n2n_isnew.first->second = newNode;
6616           myLastCreatedNodes.push_back(newNode);
6617           srcNodes.push_back( node );
6618         }
6619         else {
6620           aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6621           // node position on shape becomes invalid
6622           const_cast< SMDS_MeshNode* > ( node )->SetPosition
6623             ( SMDS_SpacePosition::originSpacePosition() );
6624         }
6625
6626         // keep inverse elements
6627         if ( !theCopy && !theTargetMesh && needReverse ) {
6628           SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6629           while ( invElemIt->more() ) {
6630             const SMDS_MeshElement* iel = invElemIt->next();
6631             inverseElemSet.insert( iel );
6632           }
6633         }
6634       }
6635     } // loop on elems in { &orphanNode, &theElems };
6636
6637   // either create new elements or reverse mirrored ones
6638   if ( !theCopy && !needReverse && !theTargetMesh )
6639     return PGroupIDs();
6640
6641   theElems.insert( inverseElemSet.begin(),inverseElemSet.end() );
6642
6643   // Replicate or reverse elements
6644
6645   std::vector<int> iForw;
6646   vector<const SMDS_MeshNode*> nodes;
6647   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6648   {
6649     const SMDS_MeshElement* elem = *itElem;
6650     if ( !elem ) continue;
6651
6652     SMDSAbs_GeometryType geomType = elem->GetGeomType();
6653     size_t               nbNodes  = elem->NbNodes();
6654     if ( geomType == SMDSGeom_NONE ) continue; // node
6655
6656     nodes.resize( nbNodes );
6657
6658     if ( geomType == SMDSGeom_POLYHEDRA )  // ------------------ polyhedral volume
6659     {
6660       const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( elem );
6661       if ( !aPolyedre )
6662         continue;
6663       nodes.clear();
6664       bool allTransformed = true;
6665       int nbFaces = aPolyedre->NbFaces();
6666       for (int iface = 1; iface <= nbFaces && allTransformed; iface++)
6667       {
6668         int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6669         for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++)
6670         {
6671           const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6672           TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6673           if ( nodeMapIt == nodeMap.end() )
6674             allTransformed = false; // not all nodes transformed
6675           else
6676             nodes.push_back((*nodeMapIt).second);
6677         }
6678         if ( needReverse && allTransformed )
6679           std::reverse( nodes.end() - nbFaceNodes, nodes.end() );
6680       }
6681       if ( !allTransformed )
6682         continue; // not all nodes transformed
6683     }
6684     else // ----------------------- the rest element types
6685     {
6686       while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6687       const vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType(), nbNodes );
6688       const vector<int>&    i = needReverse ? iRev : iForw;
6689
6690       // find transformed nodes
6691       size_t iNode = 0;
6692       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6693       while ( itN->more() ) {
6694         const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
6695         TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6696         if ( nodeMapIt == nodeMap.end() )
6697           break; // not all nodes transformed
6698         nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
6699       }
6700       if ( iNode != nbNodes )
6701         continue; // not all nodes transformed
6702     }
6703
6704     if ( editor ) {
6705       // copy in this or a new mesh
6706       if ( editor->AddElement( nodes, elemType.Init( elem, /*basicOnly=*/false )))
6707         srcElems.push_back( elem );
6708     }
6709     else {
6710       // reverse element as it was reversed by transformation
6711       if ( nbNodes > 2 )
6712         aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
6713     }
6714
6715   } // loop on elements
6716
6717   if ( editor && editor != this )
6718     myLastCreatedElems.swap( editor->myLastCreatedElems );
6719
6720   PGroupIDs newGroupIDs;
6721
6722   if ( ( theMakeGroups && theCopy ) ||
6723        ( theMakeGroups && theTargetMesh ) )
6724     newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh, false );
6725
6726   return newGroupIDs;
6727 }
6728
6729 //================================================================================
6730 /*!
6731  * \brief Make an offset mesh from a source 2D mesh
6732  *  \param [in] theElements - source faces
6733  *  \param [in] theValue - offset value
6734  *  \param [out] theTgtMesh - a mesh to add offset elements to
6735  *  \param [in] theMakeGroups - to generate groups
6736  *  \return PGroupIDs - IDs of created groups
6737  */
6738 //================================================================================
6739
6740 SMESH_MeshEditor::PGroupIDs SMESH_MeshEditor::Offset( TIDSortedElemSet & theElements,
6741                                                       const double       theValue,
6742                                                       SMESH_Mesh*        theTgtMesh,
6743                                                       const bool         theMakeGroups,
6744                                                       const bool         theCopyElements,
6745                                                       const bool         theFixSelfIntersection)
6746 {
6747   SMESHDS_Mesh*    meshDS = GetMeshDS();
6748   SMESHDS_Mesh* tgtMeshDS = theTgtMesh->GetMeshDS();
6749   SMESH_MeshEditor tgtEditor( theTgtMesh );
6750
6751   SMDS_ElemIteratorPtr eIt;
6752   if ( theElements.empty() ) eIt = meshDS->elementsIterator( SMDSAbs_Face );
6753   else                       eIt = SMESHUtils::elemSetIterator( theElements );
6754
6755   SMESH_MeshAlgos::TEPairVec new2OldFaces;
6756   SMESH_MeshAlgos::TNPairVec new2OldNodes;
6757   std::unique_ptr< SMDS_Mesh > offsetMesh
6758     ( SMESH_MeshAlgos::MakeOffset( eIt, *meshDS, theValue,
6759                                    theFixSelfIntersection,
6760                                    new2OldFaces, new2OldNodes ));
6761   if ( offsetMesh->NbElements() == 0 )
6762     return PGroupIDs(); // MakeOffset() failed
6763
6764
6765   if ( theTgtMesh == myMesh && !theCopyElements )
6766   {
6767     // clear the source elements
6768     if ( theElements.empty() ) eIt = meshDS->elementsIterator( SMDSAbs_Face );
6769     else                       eIt = SMESHUtils::elemSetIterator( theElements );
6770     while ( eIt->more() )
6771       meshDS->RemoveFreeElement( eIt->next(), 0 );
6772   }
6773
6774   offsetMesh->Modified();
6775   offsetMesh->CompactMesh(); // make IDs start from 1
6776
6777   // source elements for each generated one
6778   SMESH_SequenceOfElemPtr srcElems, srcNodes;
6779   srcElems.reserve( new2OldFaces.size() );
6780   srcNodes.reserve( new2OldNodes.size() );
6781
6782   ClearLastCreated();
6783   myLastCreatedElems.reserve( new2OldFaces.size() );
6784   myLastCreatedNodes.reserve( new2OldNodes.size() );
6785
6786   // copy offsetMesh to theTgtMesh
6787
6788   int idShift = meshDS->MaxNodeID();
6789   for ( size_t i = 0; i < new2OldNodes.size(); ++i )
6790     if ( const SMDS_MeshNode* n = new2OldNodes[ i ].first )
6791     {
6792       if ( n->NbInverseElements() > 0 )
6793       {
6794         const SMDS_MeshNode* n2 =
6795           tgtMeshDS->AddNodeWithID( n->X(), n->Y(), n->Z(), idShift + n->GetID() );
6796         myLastCreatedNodes.push_back( n2 );
6797         srcNodes.push_back( new2OldNodes[ i ].second );
6798       }
6799     }
6800
6801   ElemFeatures elemType;
6802   for ( size_t i = 0; i < new2OldFaces.size(); ++i )
6803     if ( const SMDS_MeshElement* f = new2OldFaces[ i ].first )
6804     {
6805       elemType.Init( f );
6806       elemType.myNodes.clear();
6807       for ( SMDS_NodeIteratorPtr nIt = f->nodeIterator(); nIt->more(); )
6808       {
6809         const SMDS_MeshNode* n2 = nIt->next();
6810         elemType.myNodes.push_back( tgtMeshDS->FindNode( idShift + n2->GetID() ));
6811       }
6812       tgtEditor.AddElement( elemType.myNodes, elemType );
6813       srcElems.push_back( new2OldFaces[ i ].second );
6814     }
6815
6816   myLastCreatedElems.swap( tgtEditor.myLastCreatedElems );
6817
6818   PGroupIDs newGroupIDs;
6819   if ( theMakeGroups )
6820     newGroupIDs = generateGroups( srcNodes, srcElems, "offset", theTgtMesh, false );
6821
6822   return newGroupIDs;
6823 }
6824
6825 //=======================================================================
6826 /*!
6827  * \brief Create groups of elements made during transformation
6828  *  \param nodeGens - nodes making corresponding myLastCreatedNodes
6829  *  \param elemGens - elements making corresponding myLastCreatedElems
6830  *  \param postfix - to push_back to names of new groups
6831  *  \param targetMesh - mesh to create groups in
6832  *  \param topPresent - is there are "top" elements that are created by sweeping
6833  */
6834 //=======================================================================
6835
6836 SMESH_MeshEditor::PGroupIDs
6837 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
6838                                  const SMESH_SequenceOfElemPtr& elemGens,
6839                                  const std::string&             postfix,
6840                                  SMESH_Mesh*                    targetMesh,
6841                                  const bool                     topPresent)
6842 {
6843   PGroupIDs newGroupIDs( new list<int> );
6844   SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6845
6846   // Sort existing groups by types and collect their names
6847
6848   // containers to store an old group and generated new ones;
6849   // 1st new group is for result elems of different type than a source one;
6850   // 2nd new group is for same type result elems ("top" group at extrusion)
6851   using boost::tuple;
6852   using boost::make_tuple;
6853   typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
6854   vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
6855   vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
6856   // group names
6857   set< string > groupNames;
6858
6859   SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
6860   if ( !groupIt->more() ) return newGroupIDs;
6861
6862   int newGroupID = mesh->GetGroupIds().back()+1;
6863   while ( groupIt->more() )
6864   {
6865     SMESH_Group * group = groupIt->next();
6866     if ( !group ) continue;
6867     SMESHDS_GroupBase* groupDS = group->GetGroupDS();
6868     if ( !groupDS || groupDS->IsEmpty() ) continue;
6869     groupNames.insert    ( group->GetName() );
6870     groupDS->SetStoreName( group->GetName() );
6871     const SMDSAbs_ElementType type = groupDS->GetType();
6872     SMESHDS_Group* newGroup    = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6873     SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6874     groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
6875     orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
6876   }
6877
6878   // Loop on nodes and elements to add them in new groups
6879
6880   vector< const SMDS_MeshElement* > resultElems;
6881   for ( int isNodes = 0; isNodes < 2; ++isNodes )
6882   {
6883     const SMESH_SequenceOfElemPtr& gens  = isNodes ? nodeGens : elemGens;
6884     const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
6885     if ( gens.size() != elems.size() )
6886       throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
6887
6888     // loop on created elements
6889     for (size_t iElem = 0; iElem < elems.size(); ++iElem )
6890     {
6891       const SMDS_MeshElement* sourceElem = gens[ iElem ];
6892       if ( !sourceElem ) {
6893         MESSAGE("generateGroups(): NULL source element");
6894         continue;
6895       }
6896       list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
6897       if ( groupsOldNew.empty() ) { // no groups of this type at all
6898         while ( iElem+1 < gens.size() && gens[ iElem+1 ] == sourceElem )
6899           ++iElem; // skip all elements made by sourceElem
6900         continue;
6901       }
6902       // collect all elements made by the iElem-th sourceElem
6903       resultElems.clear();
6904       if ( const SMDS_MeshElement* resElem = elems[ iElem ])
6905         if ( resElem != sourceElem )
6906           resultElems.push_back( resElem );
6907       while ( iElem+1 < gens.size() && gens[ iElem+1 ] == sourceElem )
6908         if ( const SMDS_MeshElement* resElem = elems[ ++iElem ])
6909           if ( resElem != sourceElem )
6910             resultElems.push_back( resElem );
6911
6912       const SMDS_MeshElement* topElem = 0;
6913       if ( isNodes ) // there must be a top element
6914       {
6915         topElem = resultElems.back();
6916         resultElems.pop_back();
6917       }
6918       else
6919       {
6920         vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
6921         for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
6922           if ( (*resElemIt)->GetType() == sourceElem->GetType() )
6923           {
6924             topElem = *resElemIt;
6925             *resElemIt = 0; // erase *resElemIt
6926             break;
6927           }
6928       }
6929       // add resultElems to groups originted from ones the sourceElem belongs to
6930       list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
6931       for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
6932       {
6933         SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
6934         if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
6935         {
6936           // fill in a new group
6937           SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
6938           vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
6939           for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
6940             if ( *resElemIt )
6941               newGroup.Add( *resElemIt );
6942
6943           // fill a "top" group
6944           if ( topElem )
6945           {
6946             SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
6947             newTopGroup.Add( topElem );
6948           }
6949         }
6950       }
6951     } // loop on created elements
6952   }// loop on nodes and elements
6953
6954   // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
6955
6956   list<int> topGrouIds;
6957   for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
6958   {
6959     SMESHDS_GroupBase* oldGroupDS =   orderedOldNewGroups[i]->get<0>();
6960     SMESHDS_Group*   newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
6961                                       orderedOldNewGroups[i]->get<2>() };
6962     for ( int is2nd = 0; is2nd < 2; ++is2nd )
6963     {
6964       SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
6965       if ( newGroupDS->IsEmpty() )
6966       {
6967         mesh->GetMeshDS()->RemoveGroup( newGroupDS );
6968       }
6969       else
6970       {
6971         // set group type
6972         newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
6973
6974         // make a name
6975         const bool isTop = ( topPresent &&
6976                              newGroupDS->GetType() == oldGroupDS->GetType() &&
6977                              is2nd );
6978
6979         string name = oldGroupDS->GetStoreName();
6980         { // remove trailing whitespaces (issue 22599)
6981           size_t size = name.size();
6982           while ( size > 1 && isspace( name[ size-1 ]))
6983             --size;
6984           if ( size != name.size() )
6985           {
6986             name.resize( size );
6987             oldGroupDS->SetStoreName( name.c_str() );
6988           }
6989         }
6990         if ( !targetMesh ) {
6991           string suffix = ( isTop ? "top": postfix.c_str() );
6992           name += "_";
6993           name += suffix;
6994           int nb = 1;
6995           while ( !groupNames.insert( name ).second ) // name exists
6996             name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
6997         }
6998         else if ( isTop ) {
6999           name += "_top";
7000         }
7001         newGroupDS->SetStoreName( name.c_str() );
7002
7003         // make a SMESH_Groups
7004         mesh->AddGroup( newGroupDS );
7005         if ( isTop )
7006           topGrouIds.push_back( newGroupDS->GetID() );
7007         else
7008           newGroupIDs->push_back( newGroupDS->GetID() );
7009       }
7010     }
7011   }
7012   newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
7013
7014   return newGroupIDs;
7015 }
7016
7017 //================================================================================
7018 /*!
7019  *  * \brief Return list of group of nodes close to each other within theTolerance
7020  *  *        Search among theNodes or in the whole mesh if theNodes is empty using
7021  *  *        an Octree algorithm
7022  *  \param [in,out] theNodes - the nodes to treat
7023  *  \param [in]     theTolerance - the tolerance
7024  *  \param [out]    theGroupsOfNodes - the result groups of coincident nodes
7025  *  \param [in]     theSeparateCornersAndMedium - if \c true, in quadratic mesh puts 
7026  *         corner and medium nodes in separate groups
7027  */
7028 //================================================================================
7029
7030 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet &   theNodes,
7031                                             const double         theTolerance,
7032                                             TListOfListOfNodes & theGroupsOfNodes,
7033                                             bool                 theSeparateCornersAndMedium)
7034 {
7035   ClearLastCreated();
7036
7037   if ( myMesh->NbEdges  ( ORDER_QUADRATIC ) +
7038        myMesh->NbFaces  ( ORDER_QUADRATIC ) +
7039        myMesh->NbVolumes( ORDER_QUADRATIC ) == 0 )
7040     theSeparateCornersAndMedium = false;
7041
7042   TIDSortedNodeSet& corners = theNodes;
7043   TIDSortedNodeSet  medium;
7044
7045   if ( theNodes.empty() ) // get all nodes in the mesh
7046   {
7047     TIDSortedNodeSet* nodes[2] = { &corners, &medium };
7048     SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator();
7049     if ( theSeparateCornersAndMedium )
7050       while ( nIt->more() )
7051       {
7052         const SMDS_MeshNode* n = nIt->next();
7053         TIDSortedNodeSet* & nodeSet = nodes[ SMESH_MesherHelper::IsMedium( n )];
7054         nodeSet->insert( nodeSet->end(), n );
7055       }
7056     else
7057       while ( nIt->more() )
7058         theNodes.insert( theNodes.end(), nIt->next() );
7059   }
7060   else if ( theSeparateCornersAndMedium ) // separate corners from medium nodes
7061   {
7062     TIDSortedNodeSet::iterator nIt = corners.begin();
7063     while ( nIt != corners.end() )
7064       if ( SMESH_MesherHelper::IsMedium( *nIt ))
7065       {
7066         medium.insert( medium.end(), *nIt );
7067         corners.erase( nIt++ );
7068       }
7069       else
7070       {
7071         ++nIt;
7072       }
7073   }
7074
7075   if ( !corners.empty() )
7076     SMESH_OctreeNode::FindCoincidentNodes ( corners, &theGroupsOfNodes, theTolerance );
7077   if ( !medium.empty() )
7078     SMESH_OctreeNode::FindCoincidentNodes ( medium, &theGroupsOfNodes, theTolerance );
7079 }
7080
7081 //=======================================================================
7082 //function : SimplifyFace
7083 //purpose  : split a chain of nodes into several closed chains
7084 //=======================================================================
7085
7086 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
7087                                     vector<const SMDS_MeshNode *>&       poly_nodes,
7088                                     vector<int>&                         quantities) const
7089 {
7090   int nbNodes = faceNodes.size();
7091   while ( faceNodes[ 0 ] == faceNodes[ nbNodes-1 ] && nbNodes > 2 )
7092     --nbNodes;
7093   if ( nbNodes < 3 )
7094     return 0;
7095   size_t prevNbQuant = quantities.size();
7096
7097   vector< const SMDS_MeshNode* > simpleNodes; simpleNodes.reserve( nbNodes );
7098   map< const SMDS_MeshNode*, int > nodeIndices; // indices within simpleNodes
7099   map< const SMDS_MeshNode*, int >::iterator nInd;
7100
7101   nodeIndices.insert( make_pair( faceNodes[0], 0 ));
7102   simpleNodes.push_back( faceNodes[0] );
7103   for ( int iCur = 1; iCur < nbNodes; iCur++ )
7104   {
7105     if ( faceNodes[ iCur ] != simpleNodes.back() )
7106     {
7107       int index = simpleNodes.size();
7108       nInd = nodeIndices.insert( make_pair( faceNodes[ iCur ], index )).first;
7109       int prevIndex = nInd->second;
7110       if ( prevIndex < index )
7111       {
7112         // a sub-loop found
7113         int loopLen = index - prevIndex;
7114         if ( loopLen > 2 )
7115         {
7116           // store the sub-loop
7117           quantities.push_back( loopLen );
7118           for ( int i = prevIndex; i < index; i++ )
7119             poly_nodes.push_back( simpleNodes[ i ]);
7120         }
7121         simpleNodes.resize( prevIndex+1 );
7122       }
7123       else
7124       {
7125         simpleNodes.push_back( faceNodes[ iCur ]);
7126       }
7127     }
7128   }
7129
7130   if ( simpleNodes.size() > 2 )
7131   {
7132     quantities.push_back( simpleNodes.size() );
7133     poly_nodes.insert ( poly_nodes.end(), simpleNodes.begin(), simpleNodes.end() );
7134   }
7135
7136   return quantities.size() - prevNbQuant;
7137 }
7138
7139 //=======================================================================
7140 //function : MergeNodes
7141 //purpose  : In each group, the cdr of nodes are substituted by the first one
7142 //           in all elements.
7143 //=======================================================================
7144
7145 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes,
7146                                    const bool           theAvoidMakingHoles)
7147 {
7148   ClearLastCreated();
7149
7150   SMESHDS_Mesh* mesh = GetMeshDS();
7151
7152   TNodeNodeMap nodeNodeMap; // node to replace - new node
7153   set<const SMDS_MeshElement*> elems; // all elements with changed nodes
7154   list< int > rmElemIds, rmNodeIds;
7155   vector< ElemFeatures > newElemDefs;
7156
7157   // Fill nodeNodeMap and elems
7158
7159   TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
7160   for ( ; grIt != theGroupsOfNodes.end(); grIt++ )
7161   {
7162     list<const SMDS_MeshNode*>& nodes = *grIt;
7163     list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
7164     const SMDS_MeshNode* nToKeep = *nIt;
7165     for ( ++nIt; nIt != nodes.end(); nIt++ )
7166     {
7167       const SMDS_MeshNode* nToRemove = *nIt;
7168       nodeNodeMap.insert( make_pair( nToRemove, nToKeep ));
7169       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
7170       while ( invElemIt->more() ) {
7171         const SMDS_MeshElement* elem = invElemIt->next();
7172         elems.insert(elem);
7173       }
7174     }
7175   }
7176
7177   // Apply recursive replacements (BUG 0020185)
7178   TNodeNodeMap::iterator nnIt = nodeNodeMap.begin();
7179   for ( ; nnIt != nodeNodeMap.end(); ++nnIt )
7180   {
7181     const SMDS_MeshNode* nToKeep = nnIt->second;
7182     TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( nToKeep );
7183     while ( nnIt_i != nodeNodeMap.end() && nnIt_i->second != nnIt->second )
7184     {
7185       nToKeep = nnIt_i->second;
7186       nnIt->second = nToKeep;
7187       nnIt_i = nodeNodeMap.find( nToKeep );
7188     }
7189   }
7190
7191   if ( theAvoidMakingHoles )
7192   {
7193     // find elements whose topology changes
7194
7195     vector<const SMDS_MeshElement*> pbElems;
7196     set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7197     for ( ; eIt != elems.end(); ++eIt )
7198     {
7199       const SMDS_MeshElement* elem = *eIt;
7200       SMDS_ElemIteratorPtr     itN = elem->nodesIterator();
7201       while ( itN->more() )
7202       {
7203         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7204         TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7205         if ( nnIt != nodeNodeMap.end() && elem->GetNodeIndex( nnIt->second ) >= 0 )
7206         {
7207           // several nodes of elem stick
7208           pbElems.push_back( elem );
7209           break;
7210         }
7211       }
7212     }
7213     // exclude from merge nodes causing spoiling element
7214     for ( size_t iLoop = 0; iLoop < pbElems.size(); ++iLoop ) // avoid infinite cycle
7215     {
7216       bool nodesExcluded = false;
7217       for ( size_t i = 0; i < pbElems.size(); ++i )
7218       {
7219         size_t prevNbMergeNodes = nodeNodeMap.size();
7220         if ( !applyMerge( pbElems[i], newElemDefs, nodeNodeMap, /*noHoles=*/true ) &&
7221              prevNbMergeNodes < nodeNodeMap.size() )
7222           nodesExcluded = true;
7223       }
7224       if ( !nodesExcluded )
7225         break;
7226     }
7227   }
7228
7229   for ( nnIt = nodeNodeMap.begin(); nnIt != nodeNodeMap.end(); ++nnIt )
7230   {
7231     const SMDS_MeshNode* nToRemove = nnIt->first;
7232     const SMDS_MeshNode* nToKeep   = nnIt->second;
7233     if ( nToRemove != nToKeep )
7234     {
7235       rmNodeIds.push_back( nToRemove->GetID() );
7236       AddToSameGroups( nToKeep, nToRemove, mesh );
7237       // set _alwaysComputed to a sub-mesh of VERTEX to enable further mesh computing
7238       // w/o creating node in place of merged ones.
7239       SMDS_PositionPtr pos = nToRemove->GetPosition();
7240       if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
7241         if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
7242           sm->SetIsAlwaysComputed( true );
7243     }
7244   }
7245
7246   // Change element nodes or remove an element
7247
7248   set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7249   for ( ; eIt != elems.end(); eIt++ )
7250   {
7251     const SMDS_MeshElement* elem = *eIt;
7252     SMESHDS_SubMesh*          sm = mesh->MeshElements( elem->getshapeId() );
7253
7254     bool keepElem = applyMerge( elem, newElemDefs, nodeNodeMap, /*noHoles=*/false );
7255     if ( !keepElem )
7256       rmElemIds.push_back( elem->GetID() );
7257
7258     for ( size_t i = 0; i < newElemDefs.size(); ++i )
7259     {
7260       if ( i > 0 || !mesh->ChangeElementNodes( elem,
7261                                                & newElemDefs[i].myNodes[0],
7262                                                newElemDefs[i].myNodes.size() ))
7263       {
7264         if ( i == 0 )
7265         {
7266           newElemDefs[i].SetID( elem->GetID() );
7267           mesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
7268           if ( !keepElem ) rmElemIds.pop_back();
7269         }
7270         else
7271         {
7272           newElemDefs[i].SetID( -1 );
7273         }
7274         SMDS_MeshElement* newElem = this->AddElement( newElemDefs[i].myNodes, newElemDefs[i] );
7275         if ( sm && newElem )
7276           sm->AddElement( newElem );
7277         if ( elem != newElem )
7278           ReplaceElemInGroups( elem, newElem, mesh );
7279       }
7280     }
7281   }
7282
7283   // Remove bad elements, then equal nodes (order important)
7284   Remove( rmElemIds, /*isNodes=*/false );
7285   Remove( rmNodeIds, /*isNodes=*/true );
7286
7287   return;
7288 }
7289
7290 //=======================================================================
7291 //function : applyMerge
7292 //purpose  : Compute new connectivity of an element after merging nodes
7293 //  \param [in] elems - the element
7294 //  \param [out] newElemDefs - definition(s) of result element(s)
7295 //  \param [inout] nodeNodeMap - nodes to merge
7296 //  \param [in] avoidMakingHoles - if true and and the element becomes invalid
7297 //              after merging (but not degenerated), removes nodes causing
7298 //              the invalidity from \a nodeNodeMap.
7299 //  \return bool - true if the element should be removed
7300 //=======================================================================
7301
7302 bool SMESH_MeshEditor::applyMerge( const SMDS_MeshElement* elem,
7303                                    vector< ElemFeatures >& newElemDefs,
7304                                    TNodeNodeMap&           nodeNodeMap,
7305                                    const bool              avoidMakingHoles )
7306 {
7307   bool toRemove = false; // to remove elem
7308   int nbResElems = 1;    // nb new elements
7309
7310   newElemDefs.resize(nbResElems);
7311   newElemDefs[0].Init( elem );
7312   newElemDefs[0].myNodes.clear();
7313
7314   set<const SMDS_MeshNode*> nodeSet;
7315   vector< const SMDS_MeshNode*>   curNodes;
7316   vector< const SMDS_MeshNode*> & uniqueNodes = newElemDefs[0].myNodes;
7317   vector<int> iRepl;
7318
7319   const        int  nbNodes = elem->NbNodes();
7320   SMDSAbs_EntityType entity = elem->GetEntityType();
7321
7322   curNodes.resize( nbNodes );
7323   uniqueNodes.resize( nbNodes );
7324   iRepl.resize( nbNodes );
7325   int iUnique = 0, iCur = 0, nbRepl = 0;
7326
7327   // Get new seq of nodes
7328
7329   SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7330   while ( itN->more() )
7331   {
7332     const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7333
7334     TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7335     if ( nnIt != nodeNodeMap.end() ) {
7336       n = (*nnIt).second;
7337     }
7338     curNodes[ iCur ] = n;
7339     bool isUnique = nodeSet.insert( n ).second;
7340     if ( isUnique )
7341       uniqueNodes[ iUnique++ ] = n;
7342     else
7343       iRepl[ nbRepl++ ] = iCur;
7344     iCur++;
7345   }
7346
7347   // Analyse element topology after replacement
7348
7349   int nbUniqueNodes = nodeSet.size();
7350   if ( nbNodes != nbUniqueNodes ) // some nodes stick
7351   {
7352     toRemove = true;
7353     nbResElems = 0;
7354
7355     if ( newElemDefs[0].myIsQuad && newElemDefs[0].myType == SMDSAbs_Face && nbNodes > 6 )
7356     {
7357       // if corner nodes stick, remove medium nodes between them from uniqueNodes
7358       int nbCorners = nbNodes / 2;
7359       for ( int iCur = 0; iCur < nbCorners; ++iCur )
7360       {
7361         int iNext = ( iCur + 1 ) % nbCorners;
7362         if ( curNodes[ iCur ] == curNodes[ iNext ] ) // corners stick
7363         {
7364           int iMedium = iCur + nbCorners;
7365           vector< const SMDS_MeshNode* >::iterator i =
7366             std::find( uniqueNodes.begin() + nbCorners - nbRepl,
7367                        uniqueNodes.end(),
7368                        curNodes[ iMedium ]);
7369           if ( i != uniqueNodes.end() )
7370           {
7371             --nbUniqueNodes;
7372             for ( ; i+1 != uniqueNodes.end(); ++i )
7373               *i = *(i+1);
7374           }
7375         }
7376       }
7377     }
7378
7379     switch ( entity )
7380     {
7381     case SMDSEntity_Polygon:
7382     case SMDSEntity_Quad_Polygon: // Polygon
7383     {
7384       ElemFeatures* elemType = & newElemDefs[0];
7385       const bool isQuad = elemType->myIsQuad;
7386       if ( isQuad )
7387         SMDS_MeshCell::applyInterlace // interlace medium and corner nodes
7388           ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon, nbNodes ), curNodes );
7389
7390       // a polygon can divide into several elements
7391       vector<const SMDS_MeshNode *> polygons_nodes;
7392       vector<int> quantities;
7393       nbResElems = SimplifyFace( curNodes, polygons_nodes, quantities );
7394       newElemDefs.resize( nbResElems );
7395       for ( int inode = 0, iface = 0; iface < nbResElems; iface++ )
7396       {
7397         ElemFeatures* elemType = & newElemDefs[iface];
7398         if ( iface ) elemType->Init( elem );
7399
7400         vector<const SMDS_MeshNode *>& face_nodes = elemType->myNodes;
7401         int nbNewNodes = quantities[iface];
7402         face_nodes.assign( polygons_nodes.begin() + inode,
7403                            polygons_nodes.begin() + inode + nbNewNodes );
7404         inode += nbNewNodes;
7405         if ( isQuad ) // check if a result elem is a valid quadratic polygon
7406         {
7407           bool isValid = ( nbNewNodes % 2 == 0 );
7408           for ( int i = 0; i < nbNewNodes && isValid; ++i )
7409             isValid = ( elem->IsMediumNode( face_nodes[i]) == bool( i % 2 ));
7410           elemType->SetQuad( isValid );
7411           if ( isValid ) // put medium nodes after corners
7412             SMDS_MeshCell::applyInterlaceRev
7413               ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon,
7414                                                     nbNewNodes ), face_nodes );
7415         }
7416         elemType->SetPoly(( nbNewNodes / ( elemType->myIsQuad + 1 ) > 4 ));
7417       }
7418       nbUniqueNodes = newElemDefs[0].myNodes.size();
7419       break;
7420     } // Polygon
7421
7422     case SMDSEntity_Polyhedra: // Polyhedral volume
7423     {
7424       if ( nbUniqueNodes >= 4 )
7425       {
7426         // each face has to be analyzed in order to check volume validity
7427         if ( const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( elem ))
7428         {
7429           int nbFaces = aPolyedre->NbFaces();
7430
7431           vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7432           vector<int>                  & quantities = newElemDefs[0].myPolyhedQuantities;
7433           vector<const SMDS_MeshNode *>  faceNodes;
7434           poly_nodes.clear();
7435           quantities.clear();
7436
7437           for (int iface = 1; iface <= nbFaces; iface++)
7438           {
7439             int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7440             faceNodes.resize( nbFaceNodes );
7441             for (int inode = 1; inode <= nbFaceNodes; inode++)
7442             {
7443               const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7444               TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7445               if ( nnIt != nodeNodeMap.end() ) // faceNode sticks
7446                 faceNode = (*nnIt).second;
7447               faceNodes[inode - 1] = faceNode;
7448             }
7449             SimplifyFace(faceNodes, poly_nodes, quantities);
7450           }
7451
7452           if ( quantities.size() > 3 )
7453           {
7454             // TODO: remove coincident faces
7455             nbResElems = 1;
7456             nbUniqueNodes = newElemDefs[0].myNodes.size();
7457           }
7458         }
7459       }
7460     }
7461     break;
7462
7463     // Regular elements
7464     // TODO not all the possible cases are solved. Find something more generic?
7465     case SMDSEntity_Edge: //////// EDGE
7466     case SMDSEntity_Triangle: //// TRIANGLE
7467     case SMDSEntity_Quad_Triangle:
7468     case SMDSEntity_Tetra:
7469     case SMDSEntity_Quad_Tetra: // TETRAHEDRON
7470     {
7471       break;
7472     }
7473     case SMDSEntity_Quad_Edge:
7474     {
7475       break;
7476     }
7477     case SMDSEntity_Quadrangle: //////////////////////////////////// QUADRANGLE
7478     {
7479       if ( nbUniqueNodes < 3 )
7480         toRemove = true;
7481       else if ( nbRepl == 1 && curNodes[ iRepl[0]] == curNodes[( iRepl[0]+2 )%4 ])
7482         toRemove = true; // opposite nodes stick
7483       else
7484         toRemove = false;
7485       break;
7486     }
7487     case SMDSEntity_Quad_Quadrangle: // Quadratic QUADRANGLE
7488     {
7489       //   1    5    2
7490       //    +---+---+
7491       //    |       |
7492       //   4+       +6
7493       //    |       |
7494       //    +---+---+
7495       //   0    7    3
7496       if ( nbUniqueNodes == 6 &&
7497            iRepl[0] < 4       &&
7498            ( nbRepl == 1 || iRepl[1] >= 4 ))
7499       {
7500         toRemove = false;
7501       }
7502       break;
7503     }
7504     case SMDSEntity_BiQuad_Quadrangle: // Bi-Quadratic QUADRANGLE
7505     {
7506       //   1    5    2
7507       //    +---+---+
7508       //    |       |
7509       //   4+  8+   +6
7510       //    |       |
7511       //    +---+---+
7512       //   0    7    3
7513       if ( nbUniqueNodes == 7 &&
7514            iRepl[0] < 4       &&
7515            ( nbRepl == 1 || iRepl[1] != 8 ))
7516       {
7517         toRemove = false;
7518       }
7519       break;
7520     }
7521     case SMDSEntity_Penta: ///////////////////////////////////// PENTAHEDRON
7522     {
7523       if ( nbUniqueNodes == 4 ) {
7524         // ---------------------------------> tetrahedron
7525         if ( curNodes[3] == curNodes[4] &&
7526              curNodes[3] == curNodes[5] ) {
7527           // top nodes stick
7528           toRemove = false;
7529         }
7530         else if ( curNodes[0] == curNodes[1] &&
7531                   curNodes[0] == curNodes[2] ) {
7532           // bottom nodes stick: set a top before
7533           uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7534           uniqueNodes[ 0 ] = curNodes [ 5 ];
7535           uniqueNodes[ 1 ] = curNodes [ 4 ];
7536           uniqueNodes[ 2 ] = curNodes [ 3 ];
7537           toRemove = false;
7538         }
7539         else if (( curNodes[0] == curNodes[3] ) +
7540                  ( curNodes[1] == curNodes[4] ) +
7541                  ( curNodes[2] == curNodes[5] ) == 2 ) {
7542           // a lateral face turns into a line
7543           toRemove = false;
7544         }
7545       }
7546       else if ( nbUniqueNodes == 5 ) {
7547         // PENTAHEDRON --------------------> pyramid
7548         if ( curNodes[0] == curNodes[3] )
7549         {
7550           uniqueNodes[ 0 ] = curNodes[ 1 ];
7551           uniqueNodes[ 1 ] = curNodes[ 4 ];
7552           uniqueNodes[ 2 ] = curNodes[ 5 ];
7553           uniqueNodes[ 3 ] = curNodes[ 2 ];
7554           uniqueNodes[ 4 ] = curNodes[ 0 ];
7555           toRemove = false;
7556         }
7557         if ( curNodes[1] == curNodes[4] )
7558         {
7559           uniqueNodes[ 0 ] = curNodes[ 0 ];
7560           uniqueNodes[ 1 ] = curNodes[ 2 ];
7561           uniqueNodes[ 2 ] = curNodes[ 5 ];
7562           uniqueNodes[ 3 ] = curNodes[ 3 ];
7563           uniqueNodes[ 4 ] = curNodes[ 1 ];
7564           toRemove = false;
7565         }
7566         if ( curNodes[2] == curNodes[5] )
7567         {
7568           uniqueNodes[ 0 ] = curNodes[ 0 ];
7569           uniqueNodes[ 1 ] = curNodes[ 3 ];
7570           uniqueNodes[ 2 ] = curNodes[ 4 ];
7571           uniqueNodes[ 3 ] = curNodes[ 1 ];
7572           uniqueNodes[ 4 ] = curNodes[ 2 ];
7573           toRemove = false;
7574         }
7575       }
7576       break;
7577     }
7578     case SMDSEntity_Hexa:
7579     {
7580       //////////////////////////////////// HEXAHEDRON
7581       SMDS_VolumeTool hexa (elem);
7582       hexa.SetExternalNormal();
7583       if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7584         //////////////////////// HEX ---> tetrahedron
7585         for ( int iFace = 0; iFace < 6; iFace++ ) {
7586           const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7587           if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7588               curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7589               curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7590             // one face turns into a point ...
7591             int  pickInd = ind[ 0 ];
7592             int iOppFace = hexa.GetOppFaceIndex( iFace );
7593             ind = hexa.GetFaceNodesIndices( iOppFace );
7594             int nbStick = 0;
7595             uniqueNodes.clear();
7596             for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7597               if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7598                 nbStick++;
7599               else
7600                 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7601             }
7602             if ( nbStick == 1 ) {
7603               // ... and the opposite one - into a triangle.
7604               // set a top node
7605               uniqueNodes.push_back( curNodes[ pickInd ]);
7606               toRemove = false;
7607             }
7608             break;
7609           }
7610         }
7611       }
7612       else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7613         //////////////////////// HEX ---> prism
7614         int nbTria = 0, iTria[3];
7615         const int *ind; // indices of face nodes
7616         // look for triangular faces
7617         for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7618           ind = hexa.GetFaceNodesIndices( iFace );
7619           TIDSortedNodeSet faceNodes;
7620           for ( iCur = 0; iCur < 4; iCur++ )
7621             faceNodes.insert( curNodes[ind[iCur]] );
7622           if ( faceNodes.size() == 3 )
7623             iTria[ nbTria++ ] = iFace;
7624         }
7625         // check if triangles are opposite
7626         if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7627         {
7628           // set nodes of the bottom triangle
7629           ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7630           vector<int> indB;
7631           for ( iCur = 0; iCur < 4; iCur++ )
7632             if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7633               indB.push_back( ind[iCur] );
7634           if ( !hexa.IsForward() )
7635             std::swap( indB[0], indB[2] );
7636           for ( iCur = 0; iCur < 3; iCur++ )
7637             uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7638           // set nodes of the top triangle
7639           const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7640           for ( iCur = 0; iCur < 3; ++iCur )
7641             for ( int j = 0; j < 4; ++j )
7642               if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7643               {
7644                 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7645                 break;
7646               }
7647           toRemove = false;
7648           break;
7649         }
7650       }
7651       else if (nbUniqueNodes == 5 && nbRepl == 3 ) {
7652         //////////////////// HEXAHEDRON ---> pyramid
7653         for ( int iFace = 0; iFace < 6; iFace++ ) {
7654           const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7655           if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7656               curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7657               curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7658             // one face turns into a point ...
7659             int iOppFace = hexa.GetOppFaceIndex( iFace );
7660             ind = hexa.GetFaceNodesIndices( iOppFace );
7661             uniqueNodes.clear();
7662             for ( iCur = 0; iCur < 4; iCur++ ) {
7663               if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7664                 break;
7665               else
7666                 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7667             }
7668             if ( uniqueNodes.size() == 4 ) {
7669               // ... and the opposite one is a quadrangle
7670               // set a top node
7671               const int* indTop = hexa.GetFaceNodesIndices( iFace );
7672               uniqueNodes.push_back( curNodes[indTop[ 0 ]]);
7673               toRemove = false;
7674             }
7675             break;
7676           }
7677         }
7678       }
7679
7680       if ( toRemove && nbUniqueNodes > 4 ) {
7681         ////////////////// HEXAHEDRON ---> polyhedron
7682         hexa.SetExternalNormal();
7683         vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7684         vector<int>                  & quantities = newElemDefs[0].myPolyhedQuantities;
7685         poly_nodes.reserve( 6 * 4 ); poly_nodes.clear();
7686         quantities.reserve( 6 );     quantities.clear();
7687         for ( int iFace = 0; iFace < 6; iFace++ )
7688         {
7689           const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7690           if ( curNodes[ind[0]] == curNodes[ind[2]] ||
7691                curNodes[ind[1]] == curNodes[ind[3]] )
7692           {
7693             quantities.clear();
7694             break; // opposite nodes stick
7695           }
7696           nodeSet.clear();
7697           for ( iCur = 0; iCur < 4; iCur++ )
7698           {
7699             if ( nodeSet.insert( curNodes[ind[ iCur ]] ).second )
7700               poly_nodes.push_back( curNodes[ind[ iCur ]]);
7701           }
7702           if ( nodeSet.size() < 3 )
7703             poly_nodes.resize( poly_nodes.size() - nodeSet.size() );
7704           else
7705             quantities.push_back( nodeSet.size() );
7706         }
7707         if ( quantities.size() >= 4 )
7708         {
7709           nbResElems = 1;
7710           nbUniqueNodes = poly_nodes.size();
7711           newElemDefs[0].SetPoly(true);
7712         }
7713       }
7714       break;
7715     } // case HEXAHEDRON
7716
7717     default:
7718       toRemove = true;
7719
7720     } // switch ( entity )
7721
7722     if ( toRemove && nbResElems == 0 && avoidMakingHoles )
7723     {
7724       // erase from nodeNodeMap nodes whose merge spoils elem
7725       vector< const SMDS_MeshNode* > noMergeNodes;
7726       SMESH_MeshAlgos::DeMerge( elem, curNodes, noMergeNodes );
7727       for ( size_t i = 0; i < noMergeNodes.size(); ++i )
7728         nodeNodeMap.erase( noMergeNodes[i] );
7729     }
7730     
7731   } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7732
7733   uniqueNodes.resize( nbUniqueNodes );
7734
7735   if ( !toRemove && nbResElems == 0 )
7736     nbResElems = 1;
7737
7738   newElemDefs.resize( nbResElems );
7739
7740   return !toRemove;
7741 }
7742
7743
7744 // ========================================================
7745 // class   : ComparableElement
7746 // purpose : allow comparing elements basing on their nodes
7747 // ========================================================
7748
7749 class ComparableElement : public boost::container::flat_set< int >
7750 {
7751   typedef boost::container::flat_set< int >  int_set;
7752
7753   const SMDS_MeshElement* myElem;
7754   int                     mySumID;
7755   mutable int             myGroupID;
7756
7757 public:
7758
7759   ComparableElement( const SMDS_MeshElement* theElem ):
7760     myElem ( theElem ), mySumID( 0 ), myGroupID( -1 )
7761   {
7762     this->reserve( theElem->NbNodes() );
7763     for ( SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator(); nodeIt->more(); )
7764     {
7765       int id = nodeIt->next()->GetID();
7766       mySumID += id;
7767       this->insert( id );
7768     }
7769   }
7770
7771   const SMDS_MeshElement* GetElem() const { return myElem; }
7772
7773   int& GroupID() const { return myGroupID; }
7774   //int& GroupID() const { return const_cast< int& >( myGroupID ); }
7775
7776   ComparableElement( const ComparableElement& theSource ) // move copy
7777   {
7778     ComparableElement& src = const_cast< ComparableElement& >( theSource );
7779     (int_set&) (*this ) = boost::move( src );
7780     myElem    = src.myElem;
7781     mySumID   = src.mySumID;
7782     myGroupID = src.myGroupID;
7783   }
7784
7785   static int HashCode(const ComparableElement& se, int limit )
7786   {
7787     return ::HashCode( se.mySumID, limit );
7788   }
7789   static Standard_Boolean IsEqual(const ComparableElement& se1, const ComparableElement& se2 )
7790   {
7791     return ( se1 == se2 );
7792   }
7793
7794 };
7795
7796 //=======================================================================
7797 //function : FindEqualElements
7798 //purpose  : Return list of group of elements built on the same nodes.
7799 //           Search among theElements or in the whole mesh if theElements is empty
7800 //=======================================================================
7801
7802 void SMESH_MeshEditor::FindEqualElements( TIDSortedElemSet &        theElements,
7803                                           TListOfListOfElementsID & theGroupsOfElementsID )
7804 {
7805   ClearLastCreated();
7806
7807   SMDS_ElemIteratorPtr elemIt;
7808   if ( theElements.empty() ) elemIt = GetMeshDS()->elementsIterator();
7809   else                       elemIt = SMESHUtils::elemSetIterator( theElements );
7810
7811   typedef NCollection_Map< ComparableElement, ComparableElement > TMapOfElements;
7812   typedef std::list<int>                                          TGroupOfElems;
7813   TMapOfElements               mapOfElements;
7814   std::vector< TGroupOfElems > arrayOfGroups;
7815   TGroupOfElems                groupOfElems;
7816
7817   while ( elemIt->more() )
7818   {
7819     const SMDS_MeshElement* curElem = elemIt->next();
7820     ComparableElement      compElem = curElem;
7821     // check uniqueness
7822     const ComparableElement& elemInSet = mapOfElements.Added( compElem );
7823     if ( elemInSet.GetElem() != curElem ) // coincident elem
7824     {
7825       int& iG = elemInSet.GroupID();
7826       if ( iG < 0 )
7827       {
7828         iG = arrayOfGroups.size();
7829         arrayOfGroups.push_back( groupOfElems );
7830         arrayOfGroups[ iG ].push_back( elemInSet.GetElem()->GetID() );
7831       }
7832       arrayOfGroups[ iG ].push_back( curElem->GetID() );
7833     }
7834   }
7835
7836   groupOfElems.clear();
7837   std::vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
7838   for ( ; groupIt != arrayOfGroups.end(); ++groupIt )
7839   {
7840     if ( groupIt->size() > 1 ) {
7841       //groupOfElems.sort(); -- theElements are sorted already
7842       theGroupsOfElementsID.emplace_back( *groupIt );
7843     }
7844   }
7845 }
7846
7847 //=======================================================================
7848 //function : MergeElements
7849 //purpose  : In each given group, substitute all elements by the first one.
7850 //=======================================================================
7851
7852 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
7853 {
7854   ClearLastCreated();
7855
7856   typedef list<int> TListOfIDs;
7857   TListOfIDs rmElemIds; // IDs of elems to remove
7858
7859   SMESHDS_Mesh* aMesh = GetMeshDS();
7860
7861   TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
7862   while ( groupsIt != theGroupsOfElementsID.end() ) {
7863     TListOfIDs& aGroupOfElemID = *groupsIt;
7864     aGroupOfElemID.sort();
7865     int elemIDToKeep = aGroupOfElemID.front();
7866     const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
7867     aGroupOfElemID.pop_front();
7868     TListOfIDs::iterator idIt = aGroupOfElemID.begin();
7869     while ( idIt != aGroupOfElemID.end() ) {
7870       int elemIDToRemove = *idIt;
7871       const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
7872       // add the kept element in groups of removed one (PAL15188)
7873       AddToSameGroups( elemToKeep, elemToRemove, aMesh );
7874       rmElemIds.push_back( elemIDToRemove );
7875       ++idIt;
7876     }
7877     ++groupsIt;
7878   }
7879
7880   Remove( rmElemIds, false );
7881 }
7882
7883 //=======================================================================
7884 //function : MergeEqualElements
7885 //purpose  : Remove all but one of elements built on the same nodes.
7886 //=======================================================================
7887
7888 void SMESH_MeshEditor::MergeEqualElements()
7889 {
7890   TIDSortedElemSet aMeshElements; /* empty input ==
7891                                      to merge equal elements in the whole mesh */
7892   TListOfListOfElementsID aGroupsOfElementsID;
7893   FindEqualElements( aMeshElements, aGroupsOfElementsID );
7894   MergeElements( aGroupsOfElementsID );
7895 }
7896
7897 //=======================================================================
7898 //function : findAdjacentFace
7899 //purpose  :
7900 //=======================================================================
7901
7902 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
7903                                                 const SMDS_MeshNode* n2,
7904                                                 const SMDS_MeshElement* elem)
7905 {
7906   TIDSortedElemSet elemSet, avoidSet;
7907   if ( elem )
7908     avoidSet.insert ( elem );
7909   return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
7910 }
7911
7912 //=======================================================================
7913 //function : findSegment
7914 //purpose  : Return a mesh segment by two nodes one of which can be medium
7915 //=======================================================================
7916
7917 static const SMDS_MeshElement* findSegment(const SMDS_MeshNode* n1,
7918                                            const SMDS_MeshNode* n2)
7919 {
7920   SMDS_ElemIteratorPtr it = n1->GetInverseElementIterator( SMDSAbs_Edge );
7921   while ( it->more() )
7922   {
7923     const SMDS_MeshElement* seg = it->next();
7924     if ( seg->GetNodeIndex( n2 ) >= 0 )
7925       return seg;
7926   }
7927   return 0;
7928 }
7929
7930 //=======================================================================
7931 //function : FindFreeBorder
7932 //purpose  :
7933 //=======================================================================
7934
7935 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
7936
7937 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode*             theFirstNode,
7938                                        const SMDS_MeshNode*             theSecondNode,
7939                                        const SMDS_MeshNode*             theLastNode,
7940                                        list< const SMDS_MeshNode* > &   theNodes,
7941                                        list< const SMDS_MeshElement* >& theFaces)
7942 {
7943   if ( !theFirstNode || !theSecondNode )
7944     return false;
7945   // find border face between theFirstNode and theSecondNode
7946   const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
7947   if ( !curElem )
7948     return false;
7949
7950   theFaces.push_back( curElem );
7951   theNodes.push_back( theFirstNode );
7952   theNodes.push_back( theSecondNode );
7953
7954   const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
7955   TIDSortedElemSet foundElems;
7956   bool needTheLast = ( theLastNode != 0 );
7957
7958   while ( nStart != theLastNode ) {
7959     if ( nStart == theFirstNode )
7960       return !needTheLast;
7961
7962     // find all free border faces sharing form nStart
7963
7964     list< const SMDS_MeshElement* > curElemList;
7965     list< const SMDS_MeshNode* >    nStartList;
7966     SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
7967     while ( invElemIt->more() ) {
7968       const SMDS_MeshElement* e = invElemIt->next();
7969       if ( e == curElem || foundElems.insert( e ).second ) {
7970         // get nodes
7971         int iNode = 0, nbNodes = e->NbNodes();
7972         vector<const SMDS_MeshNode*> nodes( nbNodes+1 );
7973         nodes.assign( SMDS_MeshElement::iterator( e->interlacedNodesIterator() ),
7974                       SMDS_MeshElement::iterator() );
7975         nodes.push_back( nodes[ 0 ]);
7976
7977         // check 2 links
7978         for ( iNode = 0; iNode < nbNodes; iNode++ )
7979           if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
7980                (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
7981               ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
7982           {
7983             nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
7984             curElemList.push_back( e );
7985           }
7986       }
7987     }
7988     // analyse the found
7989
7990     int nbNewBorders = curElemList.size();
7991     if ( nbNewBorders == 0 ) {
7992       // no free border furthermore
7993       return !needTheLast;
7994     }
7995     else if ( nbNewBorders == 1 ) {
7996       // one more element found
7997       nIgnore = nStart;
7998       nStart = nStartList.front();
7999       curElem = curElemList.front();
8000       theFaces.push_back( curElem );
8001       theNodes.push_back( nStart );
8002     }
8003     else {
8004       // several continuations found
8005       list< const SMDS_MeshElement* >::iterator curElemIt;
8006       list< const SMDS_MeshNode* >::iterator nStartIt;
8007       // check if one of them reached the last node
8008       if ( needTheLast ) {
8009         for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8010              curElemIt!= curElemList.end();
8011              curElemIt++, nStartIt++ )
8012           if ( *nStartIt == theLastNode ) {
8013             theFaces.push_back( *curElemIt );
8014             theNodes.push_back( *nStartIt );
8015             return true;
8016           }
8017       }
8018       // find the best free border by the continuations
8019       list<const SMDS_MeshNode*>    contNodes[ 2 ], *cNL;
8020       list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
8021       for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8022            curElemIt!= curElemList.end();
8023            curElemIt++, nStartIt++ )
8024       {
8025         cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
8026         cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
8027         // find one more free border
8028         if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
8029           cNL->clear();
8030           cFL->clear();
8031         }
8032         else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
8033           // choice: clear a worse one
8034           int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
8035           int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
8036           contNodes[ iWorse ].clear();
8037           contFaces[ iWorse ].clear();
8038         }
8039       }
8040       if ( contNodes[0].empty() && contNodes[1].empty() )
8041         return false;
8042
8043       // push_back the best free border
8044       cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
8045       cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8046       theNodes.pop_back(); // remove nIgnore
8047       theNodes.pop_back(); // remove nStart
8048       theFaces.pop_back(); // remove curElem
8049       list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
8050       list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
8051       for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
8052       for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
8053       return true;
8054
8055     } // several continuations found
8056   } // while ( nStart != theLastNode )
8057
8058   return true;
8059 }
8060
8061 //=======================================================================
8062 //function : CheckFreeBorderNodes
8063 //purpose  : Return true if the tree nodes are on a free border
8064 //=======================================================================
8065
8066 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8067                                             const SMDS_MeshNode* theNode2,
8068                                             const SMDS_MeshNode* theNode3)
8069 {
8070   list< const SMDS_MeshNode* > nodes;
8071   list< const SMDS_MeshElement* > faces;
8072   return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8073 }
8074
8075 //=======================================================================
8076 //function : SewFreeBorder
8077 //purpose  :
8078 //warning  : for border-to-side sewing theSideSecondNode is considered as
8079 //           the last side node and theSideThirdNode is not used
8080 //=======================================================================
8081
8082 SMESH_MeshEditor::Sew_Error
8083 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8084                                  const SMDS_MeshNode* theBordSecondNode,
8085                                  const SMDS_MeshNode* theBordLastNode,
8086                                  const SMDS_MeshNode* theSideFirstNode,
8087                                  const SMDS_MeshNode* theSideSecondNode,
8088                                  const SMDS_MeshNode* theSideThirdNode,
8089                                  const bool           theSideIsFreeBorder,
8090                                  const bool           toCreatePolygons,
8091                                  const bool           toCreatePolyedrs)
8092 {
8093   ClearLastCreated();
8094
8095   Sew_Error aResult = SEW_OK;
8096
8097   // ====================================
8098   //    find side nodes and elements
8099   // ====================================
8100
8101   list< const SMDS_MeshNode* >    nSide[ 2 ];
8102   list< const SMDS_MeshElement* > eSide[ 2 ];
8103   list< const SMDS_MeshNode* >::iterator    nIt[ 2 ];
8104   list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8105
8106   // Free border 1
8107   // --------------
8108   if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8109                       nSide[0], eSide[0])) {
8110     MESSAGE(" Free Border 1 not found " );
8111     aResult = SEW_BORDER1_NOT_FOUND;
8112   }
8113   if (theSideIsFreeBorder) {
8114     // Free border 2
8115     // --------------
8116     if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8117                         nSide[1], eSide[1])) {
8118       MESSAGE(" Free Border 2 not found " );
8119       aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8120     }
8121   }
8122   if ( aResult != SEW_OK )
8123     return aResult;
8124
8125   if (!theSideIsFreeBorder) {
8126     // Side 2
8127     // --------------
8128
8129     // -------------------------------------------------------------------------
8130     // Algo:
8131     // 1. If nodes to merge are not coincident, move nodes of the free border
8132     //    from the coord sys defined by the direction from the first to last
8133     //    nodes of the border to the correspondent sys of the side 2
8134     // 2. On the side 2, find the links most co-directed with the correspondent
8135     //    links of the free border
8136     // -------------------------------------------------------------------------
8137
8138     // 1. Since sewing may break if there are volumes to split on the side 2,
8139     //    we won't move nodes but just compute new coordinates for them
8140     typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8141     TNodeXYZMap nBordXYZ;
8142     list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8143     list< const SMDS_MeshNode* >::iterator nBordIt;
8144
8145     gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8146     gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8147     gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8148     gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8149     double tol2 = 1.e-8;
8150     gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8151     if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8152       // Need node movement.
8153
8154       // find X and Z axes to create trsf
8155       gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8156       gp_Vec X = Zs ^ Zb;
8157       if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8158         // Zb || Zs
8159         X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8160
8161       // coord systems
8162       gp_Ax3 toBordAx( Pb1, Zb, X );
8163       gp_Ax3 fromSideAx( Ps1, Zs, X );
8164       gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8165       // set trsf
8166       gp_Trsf toBordSys, fromSide2Sys;
8167       toBordSys.SetTransformation( toBordAx );
8168       fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8169       fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8170
8171       // move
8172       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8173         const SMDS_MeshNode* n = *nBordIt;
8174         gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8175         toBordSys.Transforms( xyz );
8176         fromSide2Sys.Transforms( xyz );
8177         nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8178       }
8179     }
8180     else {
8181       // just insert nodes XYZ in the nBordXYZ map
8182       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8183         const SMDS_MeshNode* n = *nBordIt;
8184         nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8185       }
8186     }
8187
8188     // 2. On the side 2, find the links most co-directed with the correspondent
8189     //    links of the free border
8190
8191     list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8192     list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8193     sideNodes.push_back( theSideFirstNode );
8194
8195     bool hasVolumes = false;
8196     LinkID_Gen aLinkID_Gen( GetMeshDS() );
8197     set<long> foundSideLinkIDs, checkedLinkIDs;
8198     SMDS_VolumeTool volume;
8199     //const SMDS_MeshNode* faceNodes[ 4 ];
8200
8201     const SMDS_MeshNode*    sideNode;
8202     const SMDS_MeshElement* sideElem  = 0;
8203     const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8204     const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8205     nBordIt = bordNodes.begin();
8206     nBordIt++;
8207     // border node position and border link direction to compare with
8208     gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8209     gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8210     // choose next side node by link direction or by closeness to
8211     // the current border node:
8212     bool searchByDir = ( *nBordIt != theBordLastNode );
8213     do {
8214       // find the next node on the Side 2
8215       sideNode = 0;
8216       double maxDot = -DBL_MAX, minDist = DBL_MAX;
8217       long linkID;
8218       checkedLinkIDs.clear();
8219       gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8220
8221       // loop on inverse elements of current node (prevSideNode) on the Side 2
8222       SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8223       while ( invElemIt->more() )
8224       {
8225         const SMDS_MeshElement* elem = invElemIt->next();
8226         // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8227         int iPrevNode = 0, iNode = 0, nbNodes = elem->NbNodes();
8228         vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8229         bool isVolume = volume.Set( elem );
8230         const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8231         if ( isVolume ) // --volume
8232           hasVolumes = true;
8233         else if ( elem->GetType() == SMDSAbs_Face ) { // --face
8234           // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8235           SMDS_NodeIteratorPtr nIt = elem->interlacedNodesIterator();
8236           while ( nIt->more() ) {
8237             nodes[ iNode ] = cast2Node( nIt->next() );
8238             if ( nodes[ iNode++ ] == prevSideNode )
8239               iPrevNode = iNode - 1;
8240           }
8241           // there are 2 links to check
8242           nbNodes = 2;
8243         }
8244         else // --edge
8245           continue;
8246         // loop on links, to be precise, on the second node of links
8247         for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8248           const SMDS_MeshNode* n = nodes[ iNode ];
8249           if ( isVolume ) {
8250             if ( !volume.IsLinked( n, prevSideNode ))
8251               continue;
8252           }
8253           else {
8254             if ( iNode ) // a node before prevSideNode
8255               n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8256             else         // a node after prevSideNode
8257               n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8258           }
8259           // check if this link was already used
8260           long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8261           bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8262           if (!isJustChecked &&
8263               foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8264           {
8265             // test a link geometrically
8266             gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8267             bool linkIsBetter = false;
8268             double dot = 0.0, dist = 0.0;
8269             if ( searchByDir ) { // choose most co-directed link
8270               dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8271               linkIsBetter = ( dot > maxDot );
8272             }
8273             else { // choose link with the node closest to bordPos
8274               dist = ( nextXYZ - bordPos ).SquareModulus();
8275               linkIsBetter = ( dist < minDist );
8276             }
8277             if ( linkIsBetter ) {
8278               maxDot = dot;
8279               minDist = dist;
8280               linkID = iLink;
8281               sideNode = n;
8282               sideElem = elem;
8283             }
8284           }
8285         }
8286       } // loop on inverse elements of prevSideNode
8287
8288       if ( !sideNode ) {
8289         MESSAGE(" Can't find path by links of the Side 2 ");
8290         return SEW_BAD_SIDE_NODES;
8291       }
8292       sideNodes.push_back( sideNode );
8293       sideElems.push_back( sideElem );
8294       foundSideLinkIDs.insert ( linkID );
8295       prevSideNode = sideNode;
8296
8297       if ( *nBordIt == theBordLastNode )
8298         searchByDir = false;
8299       else {
8300         // find the next border link to compare with
8301         gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8302         searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8303         // move to next border node if sideNode is before forward border node (bordPos)
8304         while ( *nBordIt != theBordLastNode && !searchByDir ) {
8305           prevBordNode = *nBordIt;
8306           nBordIt++;
8307           bordPos = nBordXYZ[ *nBordIt ];
8308           bordDir = bordPos - nBordXYZ[ prevBordNode ];
8309           searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8310         }
8311       }
8312     }
8313     while ( sideNode != theSideSecondNode );
8314
8315     if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8316       MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8317       return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8318     }
8319   } // end nodes search on the side 2
8320
8321   // ============================
8322   // sew the border to the side 2
8323   // ============================
8324
8325   int nbNodes[]  = { (int)nSide[0].size(), (int)nSide[1].size() };
8326   int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8327
8328   bool toMergeConformal = ( nbNodes[0] == nbNodes[1] );
8329   if ( toMergeConformal && toCreatePolygons )
8330   {
8331     // do not merge quadrangles if polygons are OK (IPAL0052824)
8332     eIt[0] = eSide[0].begin();
8333     eIt[1] = eSide[1].begin();
8334     bool allQuads[2] = { true, true };
8335     for ( int iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8336       for ( ; allQuads[iBord] && eIt[iBord] != eSide[iBord].end(); ++eIt[iBord] )
8337         allQuads[iBord] = ( (*eIt[iBord])->NbCornerNodes() == 4 );
8338     }
8339     toMergeConformal = ( !allQuads[0] && !allQuads[1] );
8340   }
8341
8342   TListOfListOfNodes nodeGroupsToMerge;
8343   if (( toMergeConformal ) ||
8344       ( theSideIsFreeBorder && !theSideThirdNode )) {
8345
8346     // all nodes are to be merged
8347
8348     for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8349          nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8350          nIt[0]++, nIt[1]++ )
8351     {
8352       nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8353       nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8354       nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8355     }
8356   }
8357   else {
8358
8359     // insert new nodes into the border and the side to get equal nb of segments
8360
8361     // get normalized parameters of nodes on the borders
8362     vector< double > param[ 2 ];
8363     param[0].resize( maxNbNodes );
8364     param[1].resize( maxNbNodes );
8365     int iNode, iBord;
8366     for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8367       list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8368       list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8369       const SMDS_MeshNode* nPrev = *nIt;
8370       double bordLength = 0;
8371       for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8372         const SMDS_MeshNode* nCur = *nIt;
8373         gp_XYZ segment (nCur->X() - nPrev->X(),
8374                         nCur->Y() - nPrev->Y(),
8375                         nCur->Z() - nPrev->Z());
8376         double segmentLen = segment.Modulus();
8377         bordLength += segmentLen;
8378         param[ iBord ][ iNode ] = bordLength;
8379         nPrev = nCur;
8380       }
8381       // normalize within [0,1]
8382       for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8383         param[ iBord ][ iNode ] /= bordLength;
8384       }
8385     }
8386
8387     // loop on border segments
8388     const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8389     int i[ 2 ] = { 0, 0 };
8390     nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8391     nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8392
8393     TElemOfNodeListMap insertMap;
8394     TElemOfNodeListMap::iterator insertMapIt;
8395     // insertMap is
8396     // key:   elem to insert nodes into
8397     // value: 2 nodes to insert between + nodes to be inserted
8398     do {
8399       bool next[ 2 ] = { false, false };
8400
8401       // find min adjacent segment length after sewing
8402       double nextParam = 10., prevParam = 0;
8403       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8404         if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8405           nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8406         if ( i[ iBord ] > 0 )
8407           prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8408       }
8409       double  minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8410       double  maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8411       double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8412
8413       // choose to insert or to merge nodes
8414       double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8415       if ( Abs( du ) <= minSegLen * 0.2 ) {
8416         // merge
8417         // ------
8418         nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8419         const SMDS_MeshNode* n0 = *nIt[0];
8420         const SMDS_MeshNode* n1 = *nIt[1];
8421         nodeGroupsToMerge.back().push_back( n1 );
8422         nodeGroupsToMerge.back().push_back( n0 );
8423         // position of node of the border changes due to merge
8424         param[ 0 ][ i[0] ] += du;
8425         // move n1 for the sake of elem shape evaluation during insertion.
8426         // n1 will be removed by MergeNodes() anyway
8427         const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8428         next[0] = next[1] = true;
8429       }
8430       else {
8431         // insert
8432         // ------
8433         int intoBord = ( du < 0 ) ? 0 : 1;
8434         const SMDS_MeshElement* elem = *eIt [ intoBord ];
8435         const SMDS_MeshNode*    n1   = nPrev[ intoBord ];
8436         const SMDS_MeshNode*    n2   = *nIt [ intoBord ];
8437         const SMDS_MeshNode*    nIns = *nIt [ 1 - intoBord ];
8438         if ( intoBord == 1 ) {
8439           // move node of the border to be on a link of elem of the side
8440           gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8441           gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8442           double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8443           gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8444           GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8445         }
8446         insertMapIt = insertMap.find( elem );
8447         bool  notFound = ( insertMapIt == insertMap.end() );
8448         bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8449         if ( otherLink ) {
8450           // insert into another link of the same element:
8451           // 1. perform insertion into the other link of the elem
8452           list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8453           const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8454           const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8455           InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8456           // 2. perform insertion into the link of adjacent faces
8457           while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem )) {
8458             InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8459           }
8460           while ( const SMDS_MeshElement* seg = findSegment( n12, n22 )) {
8461             InsertNodesIntoLink( seg, n12, n22, nodeList );
8462           }
8463           if (toCreatePolyedrs) {
8464             // perform insertion into the links of adjacent volumes
8465             UpdateVolumes(n12, n22, nodeList);
8466           }
8467           // 3. find an element appeared on n1 and n2 after the insertion
8468           insertMap.erase( elem );
8469           elem = findAdjacentFace( n1, n2, 0 );
8470         }
8471         if ( notFound || otherLink ) {
8472           // add element and nodes of the side into the insertMap
8473           insertMapIt = insertMap.insert( make_pair( elem, list<const SMDS_MeshNode*>() )).first;
8474           (*insertMapIt).second.push_back( n1 );
8475           (*insertMapIt).second.push_back( n2 );
8476         }
8477         // add node to be inserted into elem
8478         (*insertMapIt).second.push_back( nIns );
8479         next[ 1 - intoBord ] = true;
8480       }
8481
8482       // go to the next segment
8483       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8484         if ( next[ iBord ] ) {
8485           if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8486             eIt[ iBord ]++;
8487           nPrev[ iBord ] = *nIt[ iBord ];
8488           nIt[ iBord ]++; i[ iBord ]++;
8489         }
8490       }
8491     }
8492     while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8493
8494     // perform insertion of nodes into elements
8495
8496     for (insertMapIt = insertMap.begin();
8497          insertMapIt != insertMap.end();
8498          insertMapIt++ )
8499     {
8500       const SMDS_MeshElement* elem = (*insertMapIt).first;
8501       list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8502       const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8503       const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8504
8505       InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8506
8507       while ( const SMDS_MeshElement* seg = findSegment( n1, n2 )) {
8508         InsertNodesIntoLink( seg, n1, n2, nodeList );
8509       }
8510
8511       if ( !theSideIsFreeBorder ) {
8512         // look for and insert nodes into the faces adjacent to elem
8513         while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem )) {
8514           InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8515         }
8516       }
8517       if (toCreatePolyedrs) {
8518         // perform insertion into the links of adjacent volumes
8519         UpdateVolumes(n1, n2, nodeList);
8520       }
8521     }
8522   } // end: insert new nodes
8523
8524   MergeNodes ( nodeGroupsToMerge );
8525
8526
8527   // Remove coincident segments
8528
8529   // get new segments
8530   TIDSortedElemSet segments;
8531   SMESH_SequenceOfElemPtr newFaces;
8532   for ( size_t i = 0; i < myLastCreatedElems.size(); ++i )
8533   {
8534     if ( !myLastCreatedElems[i] ) continue;
8535     if ( myLastCreatedElems[i]->GetType() == SMDSAbs_Edge )
8536       segments.insert( segments.end(), myLastCreatedElems[i] );
8537     else
8538       newFaces.push_back( myLastCreatedElems[i] );
8539   }
8540   // get segments adjacent to merged nodes
8541   TListOfListOfNodes::iterator groupIt = nodeGroupsToMerge.begin();
8542   for ( ; groupIt != nodeGroupsToMerge.end(); groupIt++ )
8543   {
8544     const list<const SMDS_MeshNode*>& nodes = *groupIt;
8545     if ( nodes.front()->IsNull() ) continue;
8546     SMDS_ElemIteratorPtr segIt = nodes.front()->GetInverseElementIterator( SMDSAbs_Edge );
8547     while ( segIt->more() )
8548       segments.insert( segIt->next() );
8549   }
8550
8551   // find coincident
8552   TListOfListOfElementsID equalGroups;
8553   if ( !segments.empty() )
8554     FindEqualElements( segments, equalGroups );
8555   if ( !equalGroups.empty() )
8556   {
8557     // remove from segments those that will be removed
8558     TListOfListOfElementsID::iterator itGroups = equalGroups.begin();
8559     for ( ; itGroups != equalGroups.end(); ++itGroups )
8560     {
8561       list< int >& group = *itGroups;
8562       list< int >::iterator id = group.begin();
8563       for ( ++id; id != group.end(); ++id )
8564         if ( const SMDS_MeshElement* seg = GetMeshDS()->FindElement( *id ))
8565           segments.erase( seg );
8566     }
8567     // remove equal segments
8568     MergeElements( equalGroups );
8569
8570     // restore myLastCreatedElems
8571     myLastCreatedElems = newFaces;
8572     TIDSortedElemSet::iterator seg = segments.begin();
8573     for ( ; seg != segments.end(); ++seg )
8574       myLastCreatedElems.push_back( *seg );
8575   }
8576
8577   return aResult;
8578 }
8579
8580 //=======================================================================
8581 //function : InsertNodesIntoLink
8582 //purpose  : insert theNodesToInsert into theElement between theBetweenNode1
8583 //           and theBetweenNode2 and split theElement
8584 //=======================================================================
8585
8586 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement*     theElement,
8587                                            const SMDS_MeshNode*        theBetweenNode1,
8588                                            const SMDS_MeshNode*        theBetweenNode2,
8589                                            list<const SMDS_MeshNode*>& theNodesToInsert,
8590                                            const bool                  toCreatePoly)
8591 {
8592   if ( !theElement ) return;
8593
8594   SMESHDS_Mesh *aMesh = GetMeshDS();
8595   vector<const SMDS_MeshElement*> newElems;
8596
8597   if ( theElement->GetType() == SMDSAbs_Edge )
8598   {
8599     theNodesToInsert.push_front( theBetweenNode1 );
8600     theNodesToInsert.push_back ( theBetweenNode2 );
8601     list<const SMDS_MeshNode*>::iterator n = theNodesToInsert.begin();
8602     const SMDS_MeshNode* n1 = *n;
8603     for ( ++n; n != theNodesToInsert.end(); ++n )
8604     {
8605       const SMDS_MeshNode* n2 = *n;
8606       if ( const SMDS_MeshElement* seg = aMesh->FindEdge( n1, n2 ))
8607         AddToSameGroups( seg, theElement, aMesh );
8608       else
8609         newElems.push_back( aMesh->AddEdge ( n1, n2 ));
8610       n1 = n2;
8611     }
8612     theNodesToInsert.pop_front();
8613     theNodesToInsert.pop_back();
8614
8615     if ( theElement->IsQuadratic() ) // add a not split part
8616     {
8617       vector<const SMDS_MeshNode*> nodes( theElement->begin_nodes(),
8618                                           theElement->end_nodes() );
8619       int iOther = 0, nbN = nodes.size();
8620       for ( ; iOther < nbN; ++iOther )
8621         if ( nodes[iOther] != theBetweenNode1 &&
8622              nodes[iOther] != theBetweenNode2 )
8623           break;
8624       if      ( iOther == 0 )
8625       {
8626         if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[0], nodes[1] ))
8627           AddToSameGroups( seg, theElement, aMesh );
8628         else
8629           newElems.push_back( aMesh->AddEdge ( nodes[0], nodes[1] ));
8630       }
8631       else if ( iOther == 2 )
8632       {
8633         if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[1], nodes[2] ))
8634           AddToSameGroups( seg, theElement, aMesh );
8635         else
8636           newElems.push_back( aMesh->AddEdge ( nodes[1], nodes[2] ));
8637       }
8638     }
8639     // treat new elements
8640     for ( size_t i = 0; i < newElems.size(); ++i )
8641       if ( newElems[i] )
8642       {
8643         aMesh->SetMeshElementOnShape( newElems[i], theElement->getshapeId() );
8644         myLastCreatedElems.push_back( newElems[i] );
8645       }
8646     ReplaceElemInGroups( theElement, newElems, aMesh );
8647     aMesh->RemoveElement( theElement );
8648     return;
8649
8650   } // if ( theElement->GetType() == SMDSAbs_Edge )
8651
8652   const SMDS_MeshElement* theFace = theElement;
8653   if ( theFace->GetType() != SMDSAbs_Face ) return;
8654
8655   // find indices of 2 link nodes and of the rest nodes
8656   int iNode = 0, il1, il2, i3, i4;
8657   il1 = il2 = i3 = i4 = -1;
8658   vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8659
8660   SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8661   while ( nodeIt->more() ) {
8662     const SMDS_MeshNode* n = nodeIt->next();
8663     if ( n == theBetweenNode1 )
8664       il1 = iNode;
8665     else if ( n == theBetweenNode2 )
8666       il2 = iNode;
8667     else if ( i3 < 0 )
8668       i3 = iNode;
8669     else
8670       i4 = iNode;
8671     nodes[ iNode++ ] = n;
8672   }
8673   if ( il1 < 0 || il2 < 0 || i3 < 0 )
8674     return ;
8675
8676   // arrange link nodes to go one after another regarding the face orientation
8677   bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8678   list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8679   if ( reverse ) {
8680     iNode = il1;
8681     il1 = il2;
8682     il2 = iNode;
8683     aNodesToInsert.reverse();
8684   }
8685   // check that not link nodes of a quadrangles are in good order
8686   int nbFaceNodes = theFace->NbNodes();
8687   if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8688     iNode = i3;
8689     i3 = i4;
8690     i4 = iNode;
8691   }
8692
8693   if (toCreatePoly || theFace->IsPoly()) {
8694
8695     iNode = 0;
8696     vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8697
8698     // add nodes of face up to first node of link
8699     bool isFLN = false;
8700     SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8701     while ( nodeIt->more() && !isFLN ) {
8702       const SMDS_MeshNode* n = nodeIt->next();
8703       poly_nodes[iNode++] = n;
8704       isFLN = ( n == nodes[il1] );
8705     }
8706     // add nodes to insert
8707     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8708     for (; nIt != aNodesToInsert.end(); nIt++) {
8709       poly_nodes[iNode++] = *nIt;
8710     }
8711     // add nodes of face starting from last node of link
8712     while ( nodeIt->more() ) {
8713       const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8714       poly_nodes[iNode++] = n;
8715     }
8716
8717     // make a new face
8718     newElems.push_back( aMesh->AddPolygonalFace( poly_nodes ));
8719   }
8720
8721   else if ( !theFace->IsQuadratic() )
8722   {
8723     // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8724     int nbLinkNodes = 2 + aNodesToInsert.size();
8725     //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8726     vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8727     linkNodes[ 0 ] = nodes[ il1 ];
8728     linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8729     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8730     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8731       linkNodes[ iNode++ ] = *nIt;
8732     }
8733     // decide how to split a quadrangle: compare possible variants
8734     // and choose which of splits to be a quadrangle
8735     int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad = 0;
8736     if ( nbFaceNodes == 3 ) {
8737       iBestQuad = nbSplits;
8738       i4 = i3;
8739     }
8740     else if ( nbFaceNodes == 4 ) {
8741       SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8742       double aBestRate = DBL_MAX;
8743       for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8744         i1 = 0; i2 = 1;
8745         double aBadRate = 0;
8746         // evaluate elements quality
8747         for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8748           if ( iSplit == iQuad ) {
8749             SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8750                                    linkNodes[ i2++ ],
8751                                    nodes[ i3 ],
8752                                    nodes[ i4 ]);
8753             aBadRate += getBadRate( &quad, aCrit );
8754           }
8755           else {
8756             SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8757                                    linkNodes[ i2++ ],
8758                                    nodes[ iSplit < iQuad ? i4 : i3 ]);
8759             aBadRate += getBadRate( &tria, aCrit );
8760           }
8761         }
8762         // choice
8763         if ( aBadRate < aBestRate ) {
8764           iBestQuad = iQuad;
8765           aBestRate = aBadRate;
8766         }
8767       }
8768     }
8769
8770     // create new elements
8771     i1 = 0; i2 = 1;
8772     for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ )
8773     {
8774       if ( iSplit == iBestQuad )
8775         newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8776                                             linkNodes[ i2++ ],
8777                                             nodes[ i3 ],
8778                                             nodes[ i4 ]));
8779       else
8780         newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8781                                             linkNodes[ i2++ ],
8782                                             nodes[ iSplit < iBestQuad ? i4 : i3 ]));
8783     }
8784
8785     const SMDS_MeshNode* newNodes[ 4 ];
8786     newNodes[ 0 ] = linkNodes[ i1 ];
8787     newNodes[ 1 ] = linkNodes[ i2 ];
8788     newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
8789     newNodes[ 3 ] = nodes[ i4 ];
8790     if (iSplit == iBestQuad)
8791       newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] ));
8792     else
8793       newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] ));
8794
8795   } // end if(!theFace->IsQuadratic())
8796
8797   else { // theFace is quadratic
8798     // we have to split theFace on simple triangles and one simple quadrangle
8799     int tmp = il1/2;
8800     int nbshift = tmp*2;
8801     // shift nodes in nodes[] by nbshift
8802     int i,j;
8803     for(i=0; i<nbshift; i++) {
8804       const SMDS_MeshNode* n = nodes[0];
8805       for(j=0; j<nbFaceNodes-1; j++) {
8806         nodes[j] = nodes[j+1];
8807       }
8808       nodes[nbFaceNodes-1] = n;
8809     }
8810     il1 = il1 - nbshift;
8811     // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
8812     //   n0      n1     n2    n0      n1     n2
8813     //     +-----+-----+        +-----+-----+
8814     //      \         /         |           |
8815     //       \       /          |           |
8816     //      n5+     +n3       n7+           +n3
8817     //         \   /            |           |
8818     //          \ /             |           |
8819     //           +              +-----+-----+
8820     //           n4           n6      n5     n4
8821
8822     // create new elements
8823     int n1,n2,n3;
8824     if ( nbFaceNodes == 6 ) { // quadratic triangle
8825       newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8826       if ( theFace->IsMediumNode(nodes[il1]) ) {
8827         // create quadrangle
8828         newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[5] ));
8829         n1 = 1;
8830         n2 = 2;
8831         n3 = 3;
8832       }
8833       else {
8834         // create quadrangle
8835         newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[5] ));
8836         n1 = 0;
8837         n2 = 1;
8838         n3 = 5;
8839       }
8840     }
8841     else { // nbFaceNodes==8 - quadratic quadrangle
8842       newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8843       newElems.push_back( aMesh->AddFace( nodes[5], nodes[6], nodes[7] ));
8844       newElems.push_back( aMesh->AddFace( nodes[5], nodes[7], nodes[3] ));
8845       if ( theFace->IsMediumNode( nodes[ il1 ])) {
8846         // create quadrangle
8847         newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[7] ));
8848         n1 = 1;
8849         n2 = 2;
8850         n3 = 3;
8851       }
8852       else {
8853         // create quadrangle
8854         newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[7] ));
8855         n1 = 0;
8856         n2 = 1;
8857         n3 = 7;
8858       }
8859     }
8860     // create needed triangles using n1,n2,n3 and inserted nodes
8861     int nbn = 2 + aNodesToInsert.size();
8862     vector<const SMDS_MeshNode*> aNodes(nbn);
8863     aNodes[0    ] = nodes[n1];
8864     aNodes[nbn-1] = nodes[n2];
8865     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8866     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8867       aNodes[iNode++] = *nIt;
8868     }
8869     for ( i = 1; i < nbn; i++ )
8870       newElems.push_back( aMesh->AddFace( aNodes[i-1], aNodes[i], nodes[n3] ));
8871   }
8872
8873   // remove the old face
8874   for ( size_t i = 0; i < newElems.size(); ++i )
8875     if ( newElems[i] )
8876     {
8877       aMesh->SetMeshElementOnShape( newElems[i], theFace->getshapeId() );
8878       myLastCreatedElems.push_back( newElems[i] );
8879     }
8880   ReplaceElemInGroups( theFace, newElems, aMesh );
8881   aMesh->RemoveElement(theFace);
8882
8883 } // InsertNodesIntoLink()
8884
8885 //=======================================================================
8886 //function : UpdateVolumes
8887 //purpose  :
8888 //=======================================================================
8889
8890 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode*        theBetweenNode1,
8891                                       const SMDS_MeshNode*        theBetweenNode2,
8892                                       list<const SMDS_MeshNode*>& theNodesToInsert)
8893 {
8894   ClearLastCreated();
8895
8896   SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
8897   while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
8898     const SMDS_MeshElement* elem = invElemIt->next();
8899
8900     // check, if current volume has link theBetweenNode1 - theBetweenNode2
8901     SMDS_VolumeTool aVolume (elem);
8902     if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
8903       continue;
8904
8905     // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
8906     int iface, nbFaces = aVolume.NbFaces();
8907     vector<const SMDS_MeshNode *> poly_nodes;
8908     vector<int> quantities (nbFaces);
8909
8910     for (iface = 0; iface < nbFaces; iface++) {
8911       int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
8912       // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
8913       const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
8914
8915       for (int inode = 0; inode < nbFaceNodes; inode++) {
8916         poly_nodes.push_back(faceNodes[inode]);
8917
8918         if (nbInserted == 0) {
8919           if (faceNodes[inode] == theBetweenNode1) {
8920             if (faceNodes[inode + 1] == theBetweenNode2) {
8921               nbInserted = theNodesToInsert.size();
8922
8923               // add nodes to insert
8924               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
8925               for (; nIt != theNodesToInsert.end(); nIt++) {
8926                 poly_nodes.push_back(*nIt);
8927               }
8928             }
8929           }
8930           else if (faceNodes[inode] == theBetweenNode2) {
8931             if (faceNodes[inode + 1] == theBetweenNode1) {
8932               nbInserted = theNodesToInsert.size();
8933
8934               // add nodes to insert in reversed order
8935               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
8936               nIt--;
8937               for (; nIt != theNodesToInsert.begin(); nIt--) {
8938                 poly_nodes.push_back(*nIt);
8939               }
8940               poly_nodes.push_back(*nIt);
8941             }
8942           }
8943           else {
8944           }
8945         }
8946       }
8947       quantities[iface] = nbFaceNodes + nbInserted;
8948     }
8949
8950     // Replace the volume
8951     SMESHDS_Mesh *aMesh = GetMeshDS();
8952
8953     if ( SMDS_MeshElement* newElem = aMesh->AddPolyhedralVolume( poly_nodes, quantities ))
8954     {
8955       aMesh->SetMeshElementOnShape( newElem, elem->getshapeId() );
8956       myLastCreatedElems.push_back( newElem );
8957       ReplaceElemInGroups( elem, newElem, aMesh );
8958     }
8959     aMesh->RemoveElement( elem );
8960   }
8961 }
8962
8963 namespace
8964 {
8965   //================================================================================
8966   /*!
8967    * \brief Transform any volume into data of SMDSEntity_Polyhedra
8968    */
8969   //================================================================================
8970
8971   void volumeToPolyhedron( const SMDS_MeshElement*         elem,
8972                            vector<const SMDS_MeshNode *> & nodes,
8973                            vector<int> &                   nbNodeInFaces )
8974   {
8975     nodes.clear();
8976     nbNodeInFaces.clear();
8977     SMDS_VolumeTool vTool ( elem );
8978     for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
8979     {
8980       const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
8981       nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
8982       nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
8983     }
8984   }
8985 }
8986
8987 //=======================================================================
8988 /*!
8989  * \brief Convert elements contained in a sub-mesh to quadratic
8990  * \return int - nb of checked elements
8991  */
8992 //=======================================================================
8993
8994 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh *   theSm,
8995                                              SMESH_MesherHelper& theHelper,
8996                                              const bool          theForce3d)
8997 {
8998   //MESSAGE("convertElemToQuadratic");
8999   int nbElem = 0;
9000   if( !theSm ) return nbElem;
9001
9002   vector<int> nbNodeInFaces;
9003   vector<const SMDS_MeshNode *> nodes;
9004   SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
9005   while(ElemItr->more())
9006   {
9007     nbElem++;
9008     const SMDS_MeshElement* elem = ElemItr->next();
9009     if( !elem ) continue;
9010
9011     // analyse a necessity of conversion
9012     const SMDSAbs_ElementType aType = elem->GetType();
9013     if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
9014       continue;
9015     const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
9016     bool hasCentralNodes = false;
9017     if ( elem->IsQuadratic() )
9018     {
9019       bool alreadyOK;
9020       switch ( aGeomType ) {
9021       case SMDSEntity_Quad_Triangle:
9022       case SMDSEntity_Quad_Quadrangle:
9023       case SMDSEntity_Quad_Hexa:
9024       case SMDSEntity_Quad_Penta:
9025         alreadyOK = !theHelper.GetIsBiQuadratic(); break;
9026
9027       case SMDSEntity_BiQuad_Triangle:
9028       case SMDSEntity_BiQuad_Quadrangle:
9029       case SMDSEntity_TriQuad_Hexa:
9030       case SMDSEntity_BiQuad_Penta:
9031         alreadyOK = theHelper.GetIsBiQuadratic();
9032         hasCentralNodes = true;
9033         break;
9034       default:
9035         alreadyOK = true;
9036       }
9037       // take into account already present medium nodes
9038       switch ( aType ) {
9039       case SMDSAbs_Volume:
9040         theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
9041       case SMDSAbs_Face:
9042         theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
9043       case SMDSAbs_Edge:
9044         theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
9045       default:;
9046       }
9047       if ( alreadyOK )
9048         continue;
9049     }
9050     // get elem data needed to re-create it
9051     //
9052     const int id      = elem->GetID();
9053     const int nbNodes = elem->NbCornerNodes();
9054     nodes.assign(elem->begin_nodes(), elem->end_nodes());
9055     if ( aGeomType == SMDSEntity_Polyhedra )
9056       nbNodeInFaces = static_cast<const SMDS_MeshVolume* >( elem )->GetQuantities();
9057     else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
9058       volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9059
9060     // remove a linear element
9061     GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9062
9063     // remove central nodes of biquadratic elements (biquad->quad conversion)
9064     if ( hasCentralNodes )
9065       for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
9066         if ( nodes[i]->NbInverseElements() == 0 )
9067           GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
9068
9069     const SMDS_MeshElement* NewElem = 0;
9070
9071     switch( aType )
9072     {
9073     case SMDSAbs_Edge :
9074     {
9075       NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9076       break;
9077     }
9078     case SMDSAbs_Face :
9079     {
9080       switch(nbNodes)
9081       {
9082       case 3:
9083         NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9084         break;
9085       case 4:
9086         NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9087         break;
9088       default:
9089         NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9090       }
9091       break;
9092     }
9093     case SMDSAbs_Volume :
9094     {
9095       switch( aGeomType )
9096       {
9097       case SMDSEntity_Tetra:
9098         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9099         break;
9100       case SMDSEntity_Pyramid:
9101         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9102         break;
9103       case SMDSEntity_Penta:
9104       case SMDSEntity_Quad_Penta:
9105       case SMDSEntity_BiQuad_Penta:
9106         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9107         break;
9108       case SMDSEntity_Hexa:
9109       case SMDSEntity_Quad_Hexa:
9110       case SMDSEntity_TriQuad_Hexa:
9111         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9112                                       nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9113         break;
9114       case SMDSEntity_Hexagonal_Prism:
9115       default:
9116         NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9117       }
9118       break;
9119     }
9120     default :
9121       continue;
9122     }
9123     ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9124     if( NewElem && NewElem->getshapeId() < 1 )
9125       theSm->AddElement( NewElem );
9126   }
9127   return nbElem;
9128 }
9129 //=======================================================================
9130 //function : ConvertToQuadratic
9131 //purpose  :
9132 //=======================================================================
9133
9134 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
9135 {
9136   //MESSAGE("ConvertToQuadratic "<< theForce3d << " " << theToBiQuad);
9137   SMESHDS_Mesh* meshDS = GetMeshDS();
9138
9139   SMESH_MesherHelper aHelper(*myMesh);
9140
9141   aHelper.SetIsQuadratic( true );
9142   aHelper.SetIsBiQuadratic( theToBiQuad );
9143   aHelper.SetElementsOnShape(true);
9144   aHelper.ToFixNodeParameters( true );
9145
9146   // convert elements assigned to sub-meshes
9147   int nbCheckedElems = 0;
9148   if ( myMesh->HasShapeToMesh() )
9149   {
9150     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9151     {
9152       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9153       while ( smIt->more() ) {
9154         SMESH_subMesh* sm = smIt->next();
9155         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9156           aHelper.SetSubShape( sm->GetSubShape() );
9157           nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9158         }
9159       }
9160     }
9161   }
9162
9163   // convert elements NOT assigned to sub-meshes
9164   int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9165   if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
9166   {
9167     aHelper.SetElementsOnShape(false);
9168     SMESHDS_SubMesh *smDS = 0;
9169
9170     // convert edges
9171     SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9172     while( aEdgeItr->more() )
9173     {
9174       const SMDS_MeshEdge* edge = aEdgeItr->next();
9175       if ( !edge->IsQuadratic() )
9176       {
9177         int                  id = edge->GetID();
9178         const SMDS_MeshNode* n1 = edge->GetNode(0);
9179         const SMDS_MeshNode* n2 = edge->GetNode(1);
9180
9181         meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9182
9183         const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9184         ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9185       }
9186       else
9187       {
9188         aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
9189       }
9190     }
9191
9192     // convert faces
9193     SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9194     while( aFaceItr->more() )
9195     {
9196       const SMDS_MeshFace* face = aFaceItr->next();
9197       if ( !face ) continue;
9198       
9199       const SMDSAbs_EntityType type = face->GetEntityType();
9200       bool alreadyOK;
9201       switch( type )
9202       {
9203       case SMDSEntity_Quad_Triangle:
9204       case SMDSEntity_Quad_Quadrangle:
9205         alreadyOK = !theToBiQuad;
9206         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9207         break;
9208       case SMDSEntity_BiQuad_Triangle:
9209       case SMDSEntity_BiQuad_Quadrangle:
9210         alreadyOK = theToBiQuad;
9211         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9212         break;
9213       default: alreadyOK = false;
9214       }
9215       if ( alreadyOK )
9216         continue;
9217
9218       const int id = face->GetID();
9219       vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9220
9221       meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9222
9223       SMDS_MeshFace * NewFace = 0;
9224       switch( type )
9225       {
9226       case SMDSEntity_Triangle:
9227       case SMDSEntity_Quad_Triangle:
9228       case SMDSEntity_BiQuad_Triangle:
9229         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9230         if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
9231           GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
9232         break;
9233
9234       case SMDSEntity_Quadrangle:
9235       case SMDSEntity_Quad_Quadrangle:
9236       case SMDSEntity_BiQuad_Quadrangle:
9237         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9238         if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
9239           GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
9240         break;
9241
9242       default:;
9243         NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9244       }
9245       ReplaceElemInGroups( face, NewFace, GetMeshDS());
9246     }
9247
9248     // convert volumes
9249     vector<int> nbNodeInFaces;
9250     SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9251     while(aVolumeItr->more())
9252     {
9253       const SMDS_MeshVolume* volume = aVolumeItr->next();
9254       if ( !volume ) continue;
9255
9256       const SMDSAbs_EntityType type = volume->GetEntityType();
9257       if ( volume->IsQuadratic() )
9258       {
9259         bool alreadyOK;
9260         switch ( type )
9261         {
9262         case SMDSEntity_Quad_Hexa:    alreadyOK = !theToBiQuad; break;
9263         case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9264         case SMDSEntity_Quad_Penta:   alreadyOK = !theToBiQuad; break;
9265         case SMDSEntity_BiQuad_Penta: alreadyOK = theToBiQuad; break;
9266         default:                      alreadyOK = true;
9267         }
9268         if ( alreadyOK )
9269         {
9270           aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
9271           continue;
9272         }
9273       }
9274       const int id = volume->GetID();
9275       vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9276       if ( type == SMDSEntity_Polyhedra )
9277         nbNodeInFaces = static_cast<const SMDS_MeshVolume* >(volume)->GetQuantities();
9278       else if ( type == SMDSEntity_Hexagonal_Prism )
9279         volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9280
9281       meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9282
9283       SMDS_MeshVolume * NewVolume = 0;
9284       switch ( type )
9285       {
9286       case SMDSEntity_Tetra:
9287         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9288         break;
9289       case SMDSEntity_Hexa:
9290       case SMDSEntity_Quad_Hexa:
9291       case SMDSEntity_TriQuad_Hexa:
9292         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9293                                       nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9294         for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
9295           if ( nodes[i]->NbInverseElements() == 0 )
9296             GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9297         break;
9298       case SMDSEntity_Pyramid:
9299         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9300                                       nodes[3], nodes[4], id, theForce3d);
9301         break;
9302       case SMDSEntity_Penta:
9303       case SMDSEntity_Quad_Penta:
9304       case SMDSEntity_BiQuad_Penta:
9305         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9306                                       nodes[3], nodes[4], nodes[5], id, theForce3d);
9307         for ( size_t i = 15; i < nodes.size(); ++i ) // rm central nodes
9308           if ( nodes[i]->NbInverseElements() == 0 )
9309             GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9310         break;
9311       case SMDSEntity_Hexagonal_Prism:
9312       default:
9313         NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9314       }
9315       ReplaceElemInGroups(volume, NewVolume, meshDS);
9316     }
9317   }
9318
9319   if ( !theForce3d )
9320   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9321     // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9322     // aHelper.FixQuadraticElements(myError);
9323     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9324   }
9325 }
9326
9327 //================================================================================
9328 /*!
9329  * \brief Makes given elements quadratic
9330  *  \param theForce3d - if true, the medium nodes will be placed in the middle of link
9331  *  \param theElements - elements to make quadratic
9332  */
9333 //================================================================================
9334
9335 void SMESH_MeshEditor::ConvertToQuadratic(const bool        theForce3d,
9336                                           TIDSortedElemSet& theElements,
9337                                           const bool        theToBiQuad)
9338 {
9339   if ( theElements.empty() ) return;
9340
9341   // we believe that all theElements are of the same type
9342   const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9343
9344   // get all nodes shared by theElements
9345   TIDSortedNodeSet allNodes;
9346   TIDSortedElemSet::iterator eIt = theElements.begin();
9347   for ( ; eIt != theElements.end(); ++eIt )
9348     allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9349
9350   // complete theElements with elements of lower dim whose all nodes are in allNodes
9351
9352   TIDSortedElemSet quadAdjacentElems    [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9353   TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9354   TIDSortedNodeSet::iterator nIt = allNodes.begin();
9355   for ( ; nIt != allNodes.end(); ++nIt )
9356   {
9357     const SMDS_MeshNode* n = *nIt;
9358     SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9359     while ( invIt->more() )
9360     {
9361       const SMDS_MeshElement*      e = invIt->next();
9362       const SMDSAbs_ElementType type = e->GetType();
9363       if ( e->IsQuadratic() )
9364       {
9365         quadAdjacentElems[ type ].insert( e );
9366
9367         bool alreadyOK;
9368         switch ( e->GetEntityType() ) {
9369         case SMDSEntity_Quad_Triangle:
9370         case SMDSEntity_Quad_Quadrangle:
9371         case SMDSEntity_Quad_Hexa:         alreadyOK = !theToBiQuad; break;
9372         case SMDSEntity_BiQuad_Triangle:
9373         case SMDSEntity_BiQuad_Quadrangle:
9374         case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; break;
9375         default:                           alreadyOK = true;
9376         }
9377         if ( alreadyOK )
9378           continue;
9379       }
9380       if ( type >= elemType )
9381         continue; // same type or more complex linear element
9382
9383       if ( !checkedAdjacentElems[ type ].insert( e ).second )
9384         continue; // e is already checked
9385
9386       // check nodes
9387       bool allIn = true;
9388       SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9389       while ( nodeIt->more() && allIn )
9390         allIn = allNodes.count( nodeIt->next() );
9391       if ( allIn )
9392         theElements.insert(e );
9393     }
9394   }
9395
9396   SMESH_MesherHelper helper(*myMesh);
9397   helper.SetIsQuadratic( true );
9398   helper.SetIsBiQuadratic( theToBiQuad );
9399
9400   // add links of quadratic adjacent elements to the helper
9401
9402   if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9403     for ( eIt  = quadAdjacentElems[SMDSAbs_Edge].begin();
9404           eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9405     {
9406       helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9407     }
9408   if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9409     for ( eIt  = quadAdjacentElems[SMDSAbs_Face].begin();
9410           eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9411     {
9412       helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9413     }
9414   if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9415     for ( eIt  = quadAdjacentElems[SMDSAbs_Volume].begin();
9416           eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9417     {
9418       helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9419     }
9420
9421   // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9422
9423   SMESHDS_Mesh*  meshDS = GetMeshDS();
9424   SMESHDS_SubMesh* smDS = 0;
9425   for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9426   {
9427     const SMDS_MeshElement* elem = *eIt;
9428
9429     bool alreadyOK;
9430     int nbCentralNodes = 0;
9431     switch ( elem->GetEntityType() ) {
9432       // linear convertible
9433     case SMDSEntity_Edge:
9434     case SMDSEntity_Triangle:
9435     case SMDSEntity_Quadrangle:
9436     case SMDSEntity_Tetra:
9437     case SMDSEntity_Pyramid:
9438     case SMDSEntity_Hexa:
9439     case SMDSEntity_Penta:             alreadyOK = false;       nbCentralNodes = 0; break;
9440       // quadratic that can become bi-quadratic
9441     case SMDSEntity_Quad_Triangle:
9442     case SMDSEntity_Quad_Quadrangle:
9443     case SMDSEntity_Quad_Hexa:         alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9444       // bi-quadratic
9445     case SMDSEntity_BiQuad_Triangle:
9446     case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9447     case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9448       // the rest
9449     default:                           alreadyOK = true;
9450     }
9451     if ( alreadyOK ) continue;
9452
9453     const SMDSAbs_ElementType type = elem->GetType();
9454     const int                   id = elem->GetID();
9455     const int              nbNodes = elem->NbCornerNodes();
9456     vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9457
9458     helper.SetSubShape( elem->getshapeId() );
9459
9460     if ( !smDS || !smDS->Contains( elem ))
9461       smDS = meshDS->MeshElements( elem->getshapeId() );
9462     meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9463
9464     SMDS_MeshElement * newElem = 0;
9465     switch( nbNodes )
9466     {
9467     case 4: // cases for most frequently used element types go first (for optimization)
9468       if ( type == SMDSAbs_Volume )
9469         newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9470       else
9471         newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9472       break;
9473     case 8:
9474       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9475                                  nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9476       break;
9477     case 3:
9478       newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], id, theForce3d);
9479       break;
9480     case 2:
9481       newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9482       break;
9483     case 5:
9484       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9485                                  nodes[4], id, theForce3d);
9486       break;
9487     case 6:
9488       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9489                                  nodes[4], nodes[5], id, theForce3d);
9490       break;
9491     default:;
9492     }
9493     ReplaceElemInGroups( elem, newElem, meshDS);
9494     if( newElem && smDS )
9495       smDS->AddElement( newElem );
9496
9497     // remove central nodes
9498     for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9499       if ( nodes[i]->NbInverseElements() == 0 )
9500         meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9501
9502   } // loop on theElements
9503
9504   if ( !theForce3d )
9505   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9506     // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9507     // helper.FixQuadraticElements( myError );
9508     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9509   }
9510 }
9511
9512 //=======================================================================
9513 /*!
9514  * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9515  * \return int - nb of checked elements
9516  */
9517 //=======================================================================
9518
9519 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh *    theSm,
9520                                      SMDS_ElemIteratorPtr theItr,
9521                                      const int            theShapeID)
9522 {
9523   int nbElem = 0;
9524   SMESHDS_Mesh* meshDS = GetMeshDS();
9525   ElemFeatures elemType;
9526   vector<const SMDS_MeshNode *> nodes;
9527
9528   while( theItr->more() )
9529   {
9530     const SMDS_MeshElement* elem = theItr->next();
9531     nbElem++;
9532     if( elem && elem->IsQuadratic())
9533     {
9534       // get elem data
9535       int nbCornerNodes = elem->NbCornerNodes();
9536       nodes.assign( elem->begin_nodes(), elem->end_nodes() );
9537
9538       elemType.Init( elem, /*basicOnly=*/false ).SetID( elem->GetID() ).SetQuad( false );
9539
9540       //remove a quadratic element
9541       if ( !theSm || !theSm->Contains( elem ))
9542         theSm = meshDS->MeshElements( elem->getshapeId() );
9543       meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9544
9545       // remove medium nodes
9546       for ( size_t i = nbCornerNodes; i < nodes.size(); ++i )
9547         if ( nodes[i]->NbInverseElements() == 0 )
9548           meshDS->RemoveFreeNode( nodes[i], theSm );
9549
9550       // add a linear element
9551       nodes.resize( nbCornerNodes );
9552       SMDS_MeshElement * newElem = AddElement( nodes, elemType );
9553       ReplaceElemInGroups(elem, newElem, meshDS);
9554       if( theSm && newElem )
9555         theSm->AddElement( newElem );
9556     }
9557   }
9558   return nbElem;
9559 }
9560
9561 //=======================================================================
9562 //function : ConvertFromQuadratic
9563 //purpose  :
9564 //=======================================================================
9565
9566 bool SMESH_MeshEditor::ConvertFromQuadratic()
9567 {
9568   int nbCheckedElems = 0;
9569   if ( myMesh->HasShapeToMesh() )
9570   {
9571     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9572     {
9573       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9574       while ( smIt->more() ) {
9575         SMESH_subMesh* sm = smIt->next();
9576         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9577           nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9578       }
9579     }
9580   }
9581
9582   int totalNbElems =
9583     GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9584   if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9585   {
9586     SMESHDS_SubMesh *aSM = 0;
9587     removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9588   }
9589
9590   return true;
9591 }
9592
9593 namespace
9594 {
9595   //================================================================================
9596   /*!
9597    * \brief Return true if all medium nodes of the element are in the node set
9598    */
9599   //================================================================================
9600
9601   bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9602   {
9603     for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9604       if ( !nodeSet.count( elem->GetNode(i) ))
9605         return false;
9606     return true;
9607   }
9608 }
9609
9610 //================================================================================
9611 /*!
9612  * \brief Makes given elements linear
9613  */
9614 //================================================================================
9615
9616 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9617 {
9618   if ( theElements.empty() ) return;
9619
9620   // collect IDs of medium nodes of theElements; some of these nodes will be removed
9621   set<int> mediumNodeIDs;
9622   TIDSortedElemSet::iterator eIt = theElements.begin();
9623   for ( ; eIt != theElements.end(); ++eIt )
9624   {
9625     const SMDS_MeshElement* e = *eIt;
9626     for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9627       mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9628   }
9629
9630   // replace given elements by linear ones
9631   SMDS_ElemIteratorPtr elemIt = SMESHUtils::elemSetIterator( theElements );
9632   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9633
9634   // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9635   // except those elements sharing medium nodes of quadratic element whose medium nodes
9636   // are not all in mediumNodeIDs
9637
9638   // get remaining medium nodes
9639   TIDSortedNodeSet mediumNodes;
9640   set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9641   for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9642     if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9643       mediumNodes.insert( mediumNodes.end(), n );
9644
9645   // find more quadratic elements to convert
9646   TIDSortedElemSet moreElemsToConvert;
9647   TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9648   for ( ; nIt != mediumNodes.end(); ++nIt )
9649   {
9650     SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9651     while ( invIt->more() )
9652     {
9653       const SMDS_MeshElement* e = invIt->next();
9654       if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9655       {
9656         // find a more complex element including e and
9657         // whose medium nodes are not in mediumNodes
9658         bool complexFound = false;
9659         for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9660         {
9661           SMDS_ElemIteratorPtr invIt2 =
9662             (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9663           while ( invIt2->more() )
9664           {
9665             const SMDS_MeshElement* eComplex = invIt2->next();
9666             if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9667             {
9668               int nbCommonNodes = SMESH_MeshAlgos::GetCommonNodes( e, eComplex ).size();
9669               if ( nbCommonNodes == e->NbNodes())
9670               {
9671                 complexFound = true;
9672                 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9673                 break;
9674               }
9675             }
9676           }
9677         }
9678         if ( !complexFound )
9679           moreElemsToConvert.insert( e );
9680       }
9681     }
9682   }
9683   elemIt = SMESHUtils::elemSetIterator( moreElemsToConvert );
9684   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9685 }
9686
9687 //=======================================================================
9688 //function : SewSideElements
9689 //purpose  :
9690 //=======================================================================
9691
9692 SMESH_MeshEditor::Sew_Error
9693 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet&    theSide1,
9694                                    TIDSortedElemSet&    theSide2,
9695                                    const SMDS_MeshNode* theFirstNode1,
9696                                    const SMDS_MeshNode* theFirstNode2,
9697                                    const SMDS_MeshNode* theSecondNode1,
9698                                    const SMDS_MeshNode* theSecondNode2)
9699 {
9700   ClearLastCreated();
9701
9702   if ( theSide1.size() != theSide2.size() )
9703     return SEW_DIFF_NB_OF_ELEMENTS;
9704
9705   Sew_Error aResult = SEW_OK;
9706   // Algo:
9707   // 1. Build set of faces representing each side
9708   // 2. Find which nodes of the side 1 to merge with ones on the side 2
9709   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9710
9711   // =======================================================================
9712   // 1. Build set of faces representing each side:
9713   // =======================================================================
9714   // a. build set of nodes belonging to faces
9715   // b. complete set of faces: find missing faces whose nodes are in set of nodes
9716   // c. create temporary faces representing side of volumes if correspondent
9717   //    face does not exist
9718
9719   SMESHDS_Mesh* aMesh = GetMeshDS();
9720   // TODO algorithm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9721   //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9722   TIDSortedElemSet             faceSet1, faceSet2;
9723   set<const SMDS_MeshElement*> volSet1,  volSet2;
9724   set<const SMDS_MeshNode*>    nodeSet1, nodeSet2;
9725   TIDSortedElemSet             * faceSetPtr[] = { &faceSet1, &faceSet2 };
9726   set<const SMDS_MeshElement*> *  volSetPtr[] = { &volSet1,  &volSet2  };
9727   set<const SMDS_MeshNode*>    * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9728   TIDSortedElemSet             * elemSetPtr[] = { &theSide1, &theSide2 };
9729   int iSide, iFace, iNode;
9730
9731   list<const SMDS_MeshElement* > tempFaceList;
9732   for ( iSide = 0; iSide < 2; iSide++ ) {
9733     set<const SMDS_MeshNode*>    * nodeSet = nodeSetPtr[ iSide ];
9734     TIDSortedElemSet             * elemSet = elemSetPtr[ iSide ];
9735     TIDSortedElemSet             * faceSet = faceSetPtr[ iSide ];
9736     set<const SMDS_MeshElement*> * volSet  = volSetPtr [ iSide ];
9737     set<const SMDS_MeshElement*>::iterator vIt;
9738     TIDSortedElemSet::iterator eIt;
9739     set<const SMDS_MeshNode*>::iterator    nIt;
9740
9741     // check that given nodes belong to given elements
9742     const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9743     const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9744     int firstIndex = -1, secondIndex = -1;
9745     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9746       const SMDS_MeshElement* elem = *eIt;
9747       if ( firstIndex  < 0 ) firstIndex  = elem->GetNodeIndex( n1 );
9748       if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9749       if ( firstIndex > -1 && secondIndex > -1 ) break;
9750     }
9751     if ( firstIndex < 0 || secondIndex < 0 ) {
9752       // we can simply return until temporary faces created
9753       return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9754     }
9755
9756     // -----------------------------------------------------------
9757     // 1a. Collect nodes of existing faces
9758     //     and build set of face nodes in order to detect missing
9759     //     faces corresponding to sides of volumes
9760     // -----------------------------------------------------------
9761
9762     set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9763
9764     // loop on the given element of a side
9765     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9766       //const SMDS_MeshElement* elem = *eIt;
9767       const SMDS_MeshElement* elem = *eIt;
9768       if ( elem->GetType() == SMDSAbs_Face ) {
9769         faceSet->insert( elem );
9770         set <const SMDS_MeshNode*> faceNodeSet;
9771         SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9772         while ( nodeIt->more() ) {
9773           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9774           nodeSet->insert( n );
9775           faceNodeSet.insert( n );
9776         }
9777         setOfFaceNodeSet.insert( faceNodeSet );
9778       }
9779       else if ( elem->GetType() == SMDSAbs_Volume )
9780         volSet->insert( elem );
9781     }
9782     // ------------------------------------------------------------------------------
9783     // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9784     // ------------------------------------------------------------------------------
9785
9786     for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9787       SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9788       while ( fIt->more() ) { // loop on faces sharing a node
9789         const SMDS_MeshElement* f = fIt->next();
9790         if ( faceSet->find( f ) == faceSet->end() ) {
9791           // check if all nodes are in nodeSet and
9792           // complete setOfFaceNodeSet if they are
9793           set <const SMDS_MeshNode*> faceNodeSet;
9794           SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9795           bool allInSet = true;
9796           while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9797             const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9798             if ( nodeSet->find( n ) == nodeSet->end() )
9799               allInSet = false;
9800             else
9801               faceNodeSet.insert( n );
9802           }
9803           if ( allInSet ) {
9804             faceSet->insert( f );
9805             setOfFaceNodeSet.insert( faceNodeSet );
9806           }
9807         }
9808       }
9809     }
9810
9811     // -------------------------------------------------------------------------
9812     // 1c. Create temporary faces representing sides of volumes if correspondent
9813     //     face does not exist
9814     // -------------------------------------------------------------------------
9815
9816     if ( !volSet->empty() ) {
9817       //int nodeSetSize = nodeSet->size();
9818
9819       // loop on given volumes
9820       for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
9821         SMDS_VolumeTool vol (*vIt);
9822         // loop on volume faces: find free faces
9823         // --------------------------------------
9824         list<const SMDS_MeshElement* > freeFaceList;
9825         for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
9826           if ( !vol.IsFreeFace( iFace ))
9827             continue;
9828           // check if there is already a face with same nodes in a face set
9829           const SMDS_MeshElement* aFreeFace = 0;
9830           const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
9831           int nbNodes = vol.NbFaceNodes( iFace );
9832           set <const SMDS_MeshNode*> faceNodeSet;
9833           vol.GetFaceNodes( iFace, faceNodeSet );
9834           bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
9835           if ( isNewFace ) {
9836             // no such a face is given but it still can exist, check it
9837             vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
9838             aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
9839           }
9840           if ( !aFreeFace ) {
9841             // create a temporary face
9842             if ( nbNodes == 3 ) {
9843               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
9844               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
9845             }
9846             else if ( nbNodes == 4 ) {
9847               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9848               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9849             }
9850             else {
9851               vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9852               //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
9853               aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
9854             }
9855             if ( aFreeFace )
9856               tempFaceList.push_back( aFreeFace );
9857           }
9858
9859           if ( aFreeFace )
9860             freeFaceList.push_back( aFreeFace );
9861
9862         } // loop on faces of a volume
9863
9864         // choose one of several free faces of a volume
9865         // --------------------------------------------
9866         if ( freeFaceList.size() > 1 ) {
9867           // choose a face having max nb of nodes shared by other elems of a side
9868           int maxNbNodes = -1;
9869           list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
9870           while ( fIt != freeFaceList.end() ) { // loop on free faces
9871             int nbSharedNodes = 0;
9872             SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9873             while ( nodeIt->more() ) { // loop on free face nodes
9874               const SMDS_MeshNode* n =
9875                 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9876               SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
9877               while ( invElemIt->more() ) {
9878                 const SMDS_MeshElement* e = invElemIt->next();
9879                 nbSharedNodes += faceSet->count( e );
9880                 nbSharedNodes += elemSet->count( e );
9881               }
9882             }
9883             if ( nbSharedNodes > maxNbNodes ) {
9884               maxNbNodes = nbSharedNodes;
9885               freeFaceList.erase( freeFaceList.begin(), fIt++ );
9886             }
9887             else if ( nbSharedNodes == maxNbNodes ) {
9888               fIt++;
9889             }
9890             else {
9891               freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
9892             }
9893           }
9894           if ( freeFaceList.size() > 1 )
9895           {
9896             // could not choose one face, use another way
9897             // choose a face most close to the bary center of the opposite side
9898             gp_XYZ aBC( 0., 0., 0. );
9899             set <const SMDS_MeshNode*> addedNodes;
9900             TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
9901             eIt = elemSet2->begin();
9902             for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
9903               SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
9904               while ( nodeIt->more() ) { // loop on free face nodes
9905                 const SMDS_MeshNode* n =
9906                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9907                 if ( addedNodes.insert( n ).second )
9908                   aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
9909               }
9910             }
9911             aBC /= addedNodes.size();
9912             double minDist = DBL_MAX;
9913             fIt = freeFaceList.begin();
9914             while ( fIt != freeFaceList.end() ) { // loop on free faces
9915               double dist = 0;
9916               SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9917               while ( nodeIt->more() ) { // loop on free face nodes
9918                 const SMDS_MeshNode* n =
9919                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9920                 gp_XYZ p( n->X(),n->Y(),n->Z() );
9921                 dist += ( aBC - p ).SquareModulus();
9922               }
9923               if ( dist < minDist ) {
9924                 minDist = dist;
9925                 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9926               }
9927               else
9928                 fIt = freeFaceList.erase( fIt++ );
9929             }
9930           }
9931         } // choose one of several free faces of a volume
9932
9933         if ( freeFaceList.size() == 1 ) {
9934           const SMDS_MeshElement* aFreeFace = freeFaceList.front();
9935           faceSet->insert( aFreeFace );
9936           // complete a node set with nodes of a found free face
9937           //           for ( iNode = 0; iNode < ; iNode++ )
9938           //             nodeSet->insert( fNodes[ iNode ] );
9939         }
9940
9941       } // loop on volumes of a side
9942
9943       //       // complete a set of faces if new nodes in a nodeSet appeared
9944       //       // ----------------------------------------------------------
9945       //       if ( nodeSetSize != nodeSet->size() ) {
9946       //         for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9947       //           SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9948       //           while ( fIt->more() ) { // loop on faces sharing a node
9949       //             const SMDS_MeshElement* f = fIt->next();
9950       //             if ( faceSet->find( f ) == faceSet->end() ) {
9951       //               // check if all nodes are in nodeSet and
9952       //               // complete setOfFaceNodeSet if they are
9953       //               set <const SMDS_MeshNode*> faceNodeSet;
9954       //               SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9955       //               bool allInSet = true;
9956       //               while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9957       //                 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9958       //                 if ( nodeSet->find( n ) == nodeSet->end() )
9959       //                   allInSet = false;
9960       //                 else
9961       //                   faceNodeSet.insert( n );
9962       //               }
9963       //               if ( allInSet ) {
9964       //                 faceSet->insert( f );
9965       //                 setOfFaceNodeSet.insert( faceNodeSet );
9966       //               }
9967       //             }
9968       //           }
9969       //         }
9970       //       }
9971     } // Create temporary faces, if there are volumes given
9972   } // loop on sides
9973
9974   if ( faceSet1.size() != faceSet2.size() ) {
9975     // delete temporary faces: they are in reverseElements of actual nodes
9976     //    SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9977     //    while ( tmpFaceIt->more() )
9978     //      aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9979     //    list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9980     //    for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9981     //      aMesh->RemoveElement(*tmpFaceIt);
9982     MESSAGE("Diff nb of faces");
9983     return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9984   }
9985
9986   // ============================================================
9987   // 2. Find nodes to merge:
9988   //              bind a node to remove to a node to put instead
9989   // ============================================================
9990
9991   TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
9992   if ( theFirstNode1 != theFirstNode2 )
9993     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9994   if ( theSecondNode1 != theSecondNode2 )
9995     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9996
9997   LinkID_Gen aLinkID_Gen( GetMeshDS() );
9998   set< long > linkIdSet; // links to process
9999   linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
10000
10001   typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
10002   list< NLink > linkList[2];
10003   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10004   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10005   // loop on links in linkList; find faces by links and append links
10006   // of the found faces to linkList
10007   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10008   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
10009   {
10010     NLink link[] = { *linkIt[0], *linkIt[1] };
10011     long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
10012     if ( !linkIdSet.count( linkID ) )
10013       continue;
10014
10015     // by links, find faces in the face sets,
10016     // and find indices of link nodes in the found faces;
10017     // in a face set, there is only one or no face sharing a link
10018     // ---------------------------------------------------------------
10019
10020     const SMDS_MeshElement* face[] = { 0, 0 };
10021     vector<const SMDS_MeshNode*> fnodes[2];
10022     int iLinkNode[2][2];
10023     TIDSortedElemSet avoidSet;
10024     for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10025       const SMDS_MeshNode* n1 = link[iSide].first;
10026       const SMDS_MeshNode* n2 = link[iSide].second;
10027       //cout << "Side " << iSide << " ";
10028       //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
10029       // find a face by two link nodes
10030       face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
10031                                                       *faceSetPtr[ iSide ], avoidSet,
10032                                                       &iLinkNode[iSide][0],
10033                                                       &iLinkNode[iSide][1] );
10034       if ( face[ iSide ])
10035       {
10036         //cout << " F " << face[ iSide]->GetID() <<endl;
10037         faceSetPtr[ iSide ]->erase( face[ iSide ]);
10038         // put face nodes to fnodes
10039         SMDS_MeshElement::iterator nIt( face[ iSide ]->interlacedNodesIterator() ), nEnd;
10040         fnodes[ iSide ].assign( nIt, nEnd );
10041         fnodes[ iSide ].push_back( fnodes[ iSide ].front());
10042       }
10043     }
10044
10045     // check similarity of elements of the sides
10046     if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10047       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10048       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10049         aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10050       }
10051       else {
10052         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10053       }
10054       break; // do not return because it's necessary to remove tmp faces
10055     }
10056
10057     // set nodes to merge
10058     // -------------------
10059
10060     if ( face[0] && face[1] )  {
10061       const int nbNodes = face[0]->NbNodes();
10062       if ( nbNodes != face[1]->NbNodes() ) {
10063         MESSAGE("Diff nb of face nodes");
10064         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10065         break; // do not return because it s necessary to remove tmp faces
10066       }
10067       bool reverse[] = { false, false }; // order of nodes in the link
10068       for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10069         // analyse link orientation in faces
10070         int i1 = iLinkNode[ iSide ][ 0 ];
10071         int i2 = iLinkNode[ iSide ][ 1 ];
10072         reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10073       }
10074       int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
10075       int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
10076       for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
10077       {
10078         nReplaceMap.insert  ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
10079                                           fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
10080       }
10081
10082       // add other links of the faces to linkList
10083       // -----------------------------------------
10084
10085       for ( iNode = 0; iNode < nbNodes; iNode++ )  {
10086         linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
10087         pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10088         if ( !iter_isnew.second ) { // already in a set: no need to process
10089           linkIdSet.erase( iter_isnew.first );
10090         }
10091         else // new in set == encountered for the first time: add
10092         {
10093           const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
10094           const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
10095           linkList[0].push_back ( NLink( n1, n2 ));
10096           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10097         }
10098       }
10099     } // 2 faces found
10100
10101     if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
10102       break;
10103
10104   } // loop on link lists
10105
10106   if ( aResult == SEW_OK &&
10107        ( //linkIt[0] != linkList[0].end() ||
10108         !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10109     MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10110              " " << (faceSetPtr[1]->empty()));
10111     aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10112   }
10113
10114   // ====================================================================
10115   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10116   // ====================================================================
10117
10118   // delete temporary faces
10119   //  SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10120   //  while ( tmpFaceIt->more() )
10121   //    aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10122   list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10123   for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10124     aMesh->RemoveElement(*tmpFaceIt);
10125
10126   if ( aResult != SEW_OK)
10127     return aResult;
10128
10129   list< int > nodeIDsToRemove;
10130   vector< const SMDS_MeshNode*> nodes;
10131   ElemFeatures elemType;
10132
10133   // loop on nodes replacement map
10134   TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10135   for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10136     if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second )
10137     {
10138       const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10139       nodeIDsToRemove.push_back( nToRemove->GetID() );
10140       // loop on elements sharing nToRemove
10141       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10142       while ( invElemIt->more() ) {
10143         const SMDS_MeshElement* e = invElemIt->next();
10144         // get a new suite of nodes: make replacement
10145         int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10146         nodes.resize( nbNodes );
10147         SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10148         while ( nIt->more() ) {
10149           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
10150           nnIt = nReplaceMap.find( n );
10151           if ( nnIt != nReplaceMap.end() ) {
10152             nbReplaced++;
10153             n = (*nnIt).second;
10154           }
10155           nodes[ i++ ] = n;
10156         }
10157         //       if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10158         //         elemIDsToRemove.push_back( e->GetID() );
10159         //       else
10160         if ( nbReplaced )
10161         {
10162           elemType.Init( e, /*basicOnly=*/false ).SetID( e->GetID() );
10163           aMesh->RemoveElement( e );
10164
10165           if ( SMDS_MeshElement* newElem = this->AddElement( nodes, elemType ))
10166           {
10167             AddToSameGroups( newElem, e, aMesh );
10168             if ( int aShapeId = e->getshapeId() )
10169               aMesh->SetMeshElementOnShape( newElem, aShapeId );
10170           }
10171         }
10172       }
10173     }
10174
10175   Remove( nodeIDsToRemove, true );
10176
10177   return aResult;
10178 }
10179
10180 //================================================================================
10181 /*!
10182  * \brief Find corresponding nodes in two sets of faces
10183  * \param theSide1 - first face set
10184  * \param theSide2 - second first face
10185  * \param theFirstNode1 - a boundary node of set 1
10186  * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10187  * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10188  * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10189  * \param nReplaceMap - output map of corresponding nodes
10190  * \return bool  - is a success or not
10191  */
10192 //================================================================================
10193
10194 #ifdef _DEBUG_
10195 //#define DEBUG_MATCHING_NODES
10196 #endif
10197
10198 SMESH_MeshEditor::Sew_Error
10199 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10200                                     set<const SMDS_MeshElement*>& theSide2,
10201                                     const SMDS_MeshNode*          theFirstNode1,
10202                                     const SMDS_MeshNode*          theFirstNode2,
10203                                     const SMDS_MeshNode*          theSecondNode1,
10204                                     const SMDS_MeshNode*          theSecondNode2,
10205                                     TNodeNodeMap &                nReplaceMap)
10206 {
10207   set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10208
10209   nReplaceMap.clear();
10210   if ( theFirstNode1 != theFirstNode2 )
10211     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10212   if ( theSecondNode1 != theSecondNode2 )
10213     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10214
10215   set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10216   linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10217
10218   list< NLink > linkList[2];
10219   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10220   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10221
10222   // loop on links in linkList; find faces by links and append links
10223   // of the found faces to linkList
10224   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10225   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10226     NLink link[] = { *linkIt[0], *linkIt[1] };
10227     if ( linkSet.find( link[0] ) == linkSet.end() )
10228       continue;
10229
10230     // by links, find faces in the face sets,
10231     // and find indices of link nodes in the found faces;
10232     // in a face set, there is only one or no face sharing a link
10233     // ---------------------------------------------------------------
10234
10235     const SMDS_MeshElement* face[] = { 0, 0 };
10236     list<const SMDS_MeshNode*> notLinkNodes[2];
10237     //bool reverse[] = { false, false }; // order of notLinkNodes
10238     int nbNodes[2];
10239     for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10240     {
10241       const SMDS_MeshNode* n1 = link[iSide].first;
10242       const SMDS_MeshNode* n2 = link[iSide].second;
10243       set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10244       set< const SMDS_MeshElement* > facesOfNode1;
10245       for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10246       {
10247         // during a loop of the first node, we find all faces around n1,
10248         // during a loop of the second node, we find one face sharing both n1 and n2
10249         const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10250         SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10251         while ( fIt->more() ) { // loop on faces sharing a node
10252           const SMDS_MeshElement* f = fIt->next();
10253           if (faceSet->find( f ) != faceSet->end() && // f is in face set
10254               ! facesOfNode1.insert( f ).second ) // f encounters twice
10255           {
10256             if ( face[ iSide ] ) {
10257               MESSAGE( "2 faces per link " );
10258               return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10259             }
10260             face[ iSide ] = f;
10261             faceSet->erase( f );
10262
10263             // get not link nodes
10264             int nbN = f->NbNodes();
10265             if ( f->IsQuadratic() )
10266               nbN /= 2;
10267             nbNodes[ iSide ] = nbN;
10268             list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10269             int i1 = f->GetNodeIndex( n1 );
10270             int i2 = f->GetNodeIndex( n2 );
10271             int iEnd = nbN, iBeg = -1, iDelta = 1;
10272             bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10273             if ( reverse ) {
10274               std::swap( iEnd, iBeg ); iDelta = -1;
10275             }
10276             int i = i2;
10277             while ( true ) {
10278               i += iDelta;
10279               if ( i == iEnd ) i = iBeg + iDelta;
10280               if ( i == i1 ) break;
10281               nodes.push_back ( f->GetNode( i ) );
10282             }
10283           }
10284         }
10285       }
10286     }
10287     // check similarity of elements of the sides
10288     if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10289       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10290       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10291         return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10292       }
10293       else {
10294         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10295       }
10296     }
10297
10298     // set nodes to merge
10299     // -------------------
10300
10301     if ( face[0] && face[1] )  {
10302       if ( nbNodes[0] != nbNodes[1] ) {
10303         MESSAGE("Diff nb of face nodes");
10304         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10305       }
10306 #ifdef DEBUG_MATCHING_NODES
10307       MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10308                 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10309                 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10310 #endif
10311       int nbN = nbNodes[0];
10312       {
10313         list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10314         list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10315         for ( int i = 0 ; i < nbN - 2; ++i ) {
10316 #ifdef DEBUG_MATCHING_NODES
10317           MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10318 #endif
10319           nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10320         }
10321       }
10322
10323       // add other links of the face 1 to linkList
10324       // -----------------------------------------
10325
10326       const SMDS_MeshElement* f0 = face[0];
10327       const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10328       for ( int i = 0; i < nbN; i++ )
10329       {
10330         const SMDS_MeshNode* n2 = f0->GetNode( i );
10331         pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10332           linkSet.insert( SMESH_TLink( n1, n2 ));
10333         if ( !iter_isnew.second ) { // already in a set: no need to process
10334           linkSet.erase( iter_isnew.first );
10335         }
10336         else // new in set == encountered for the first time: add
10337         {
10338 #ifdef DEBUG_MATCHING_NODES
10339           MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10340                     << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10341 #endif
10342           linkList[0].push_back ( NLink( n1, n2 ));
10343           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10344         }
10345         n1 = n2;
10346       }
10347     } // 2 faces found
10348   } // loop on link lists
10349
10350   return SEW_OK;
10351 }
10352
10353 namespace // automatically find theAffectedElems for DoubleNodes()
10354 {
10355   bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem );
10356
10357   //--------------------------------------------------------------------------------
10358   // Nodes shared by adjacent FissureBorder's.
10359   // 1 node  if FissureBorder separates faces
10360   // 2 nodes if FissureBorder separates volumes
10361   struct SubBorder
10362   {
10363     const SMDS_MeshNode* _nodes[2];
10364     int                  _nbNodes;
10365
10366     SubBorder( const SMDS_MeshNode* n1, const SMDS_MeshNode* n2 = 0 )
10367     {
10368       _nodes[0] = n1;
10369       _nodes[1] = n2;
10370       _nbNodes = bool( n1 ) + bool( n2 );
10371       if ( _nbNodes == 2 && n1 > n2 )
10372         std::swap( _nodes[0], _nodes[1] );
10373     }
10374     bool operator<( const SubBorder& other ) const
10375     {
10376       for ( int i = 0; i < _nbNodes; ++i )
10377       {
10378         if ( _nodes[i] < other._nodes[i] ) return true;
10379         if ( _nodes[i] > other._nodes[i] ) return false;
10380       }
10381       return false;
10382     }
10383   };
10384
10385   //--------------------------------------------------------------------------------
10386   // Map a SubBorder to all FissureBorder it bounds
10387   struct FissureBorder;
10388   typedef std::map< SubBorder, std::vector< FissureBorder* > > TBorderLinks;
10389   typedef TBorderLinks::iterator                               TMappedSub;
10390
10391   //--------------------------------------------------------------------------------
10392   /*!
10393    * \brief Element border (volume facet or face edge) at a fissure
10394    */
10395   struct FissureBorder
10396   {
10397     std::vector< const SMDS_MeshNode* > _nodes;    // border nodes
10398     const SMDS_MeshElement*             _elems[2]; // volume or face adjacent to fissure
10399
10400     std::vector< TMappedSub           > _mappedSubs;  // Sub() in TBorderLinks map
10401     std::vector< const SMDS_MeshNode* > _sortedNodes; // to compare FissureBorder's
10402
10403     FissureBorder( FissureBorder && from ) // move constructor
10404     {
10405       std::swap( _nodes,       from._nodes );
10406       std::swap( _sortedNodes, from._sortedNodes );
10407       _elems[0] = from._elems[0];
10408       _elems[1] = from._elems[1];
10409     }
10410
10411     FissureBorder( const SMDS_MeshElement*                  elemToDuplicate,
10412                    std::vector< const SMDS_MeshElement* > & adjElems)
10413       : _nodes( elemToDuplicate->NbCornerNodes() )
10414     {
10415       for ( size_t i = 0; i < _nodes.size(); ++i )
10416         _nodes[i] = elemToDuplicate->GetNode( i );
10417
10418       SMDSAbs_ElementType type = SMDSAbs_ElementType( elemToDuplicate->GetType() + 1 );
10419       findAdjacent( type, adjElems );
10420     }
10421
10422     FissureBorder( const SMDS_MeshNode**                    nodes,
10423                    const size_t                             nbNodes,
10424                    const SMDSAbs_ElementType                adjElemsType,
10425                    std::vector< const SMDS_MeshElement* > & adjElems)
10426       : _nodes( nodes, nodes + nbNodes )
10427     {
10428       findAdjacent( adjElemsType, adjElems );
10429     }
10430
10431     void findAdjacent( const SMDSAbs_ElementType                adjElemsType,
10432                        std::vector< const SMDS_MeshElement* > & adjElems)
10433     {
10434       _elems[0] = _elems[1] = 0;
10435       adjElems.clear();
10436       if ( SMDS_Mesh::GetElementsByNodes( _nodes, adjElems, adjElemsType ))
10437         for ( size_t i = 0; i < adjElems.size() && i < 2; ++i )
10438           _elems[i] = adjElems[i];
10439     }
10440
10441     bool operator<( const FissureBorder& other ) const
10442     {
10443       return GetSortedNodes() < other.GetSortedNodes();
10444     }
10445
10446     const std::vector< const SMDS_MeshNode* >& GetSortedNodes() const
10447     {
10448       if ( _sortedNodes.empty() && !_nodes.empty() )
10449       {
10450         FissureBorder* me = const_cast<FissureBorder*>( this );
10451         me->_sortedNodes = me->_nodes;
10452         std::sort( me->_sortedNodes.begin(), me->_sortedNodes.end() );
10453       }
10454       return _sortedNodes;
10455     }
10456
10457     size_t NbSub() const
10458     {
10459       return _nodes.size();
10460     }
10461
10462     SubBorder Sub(size_t i) const
10463     {
10464       return SubBorder( _nodes[i], NbSub() > 2 ? _nodes[ (i+1)%NbSub() ] : 0 );
10465     }
10466
10467     void AddSelfTo( TBorderLinks& borderLinks )
10468     {
10469       _mappedSubs.resize( NbSub() );
10470       for ( size_t i = 0; i < NbSub(); ++i )
10471       {
10472         TBorderLinks::iterator s2b =
10473           borderLinks.insert( std::make_pair( Sub(i), TBorderLinks::mapped_type() )).first;
10474         s2b->second.push_back( this );
10475         _mappedSubs[ i ] = s2b;
10476       }
10477     }
10478
10479     void Clear()
10480     {
10481       _nodes.clear();
10482     }
10483
10484     const SMDS_MeshElement* GetMarkedElem() const
10485     {
10486       if ( _nodes.empty() ) return 0; // cleared
10487       if ( _elems[0] && _elems[0]->isMarked() ) return _elems[0];
10488       if ( _elems[1] && _elems[1]->isMarked() ) return _elems[1];
10489       return 0;
10490     }
10491
10492     gp_XYZ GetNorm() const // normal to the border
10493     {
10494       gp_XYZ norm;
10495       if ( _nodes.size() == 2 )
10496       {
10497         gp_XYZ avgNorm( 0,0,0 ); // sum of normals of adjacent faces
10498         if ( SMESH_MeshAlgos::FaceNormal( _elems[0], norm ))
10499           avgNorm += norm;
10500         if ( SMESH_MeshAlgos::FaceNormal( _elems[1], norm ))
10501           avgNorm += norm;
10502
10503         gp_XYZ bordDir( SMESH_NodeXYZ( _nodes[0] ) - SMESH_NodeXYZ( _nodes[1] ));
10504         norm = bordDir ^ avgNorm;
10505       }
10506       else
10507       {
10508         SMESH_NodeXYZ p0( _nodes[0] );
10509         SMESH_NodeXYZ p1( _nodes[1] );
10510         SMESH_NodeXYZ p2( _nodes[2] );
10511         norm = ( p0 - p1 ) ^ ( p2 - p1 );
10512       }
10513       if ( isOut( _nodes[0], norm, GetMarkedElem() ))
10514         norm.Reverse();
10515
10516       return norm;
10517     }
10518
10519     void ChooseSide() // mark an _elem located at positive side of fissure
10520     {
10521       _elems[0]->setIsMarked( true );
10522       gp_XYZ norm = GetNorm();
10523       double maxX = norm.Coord(1);
10524       if ( Abs( maxX ) < Abs( norm.Coord(2)) ) maxX = norm.Coord(2);
10525       if ( Abs( maxX ) < Abs( norm.Coord(3)) ) maxX = norm.Coord(3);
10526       if ( maxX < 0 )
10527       {
10528         _elems[0]->setIsMarked( false );
10529         _elems[1]->setIsMarked( true );
10530       }
10531     }
10532
10533   }; // struct FissureBorder
10534
10535   //--------------------------------------------------------------------------------
10536   /*!
10537    * \brief Classifier of elements at fissure edge
10538    */
10539   class FissureNormal
10540   {
10541     std::vector< gp_XYZ > _normals;
10542     bool                  _bothIn;
10543
10544   public:
10545     void Add( const SMDS_MeshNode* n, const FissureBorder& bord )
10546     {
10547       _bothIn = false;
10548       _normals.reserve(2);
10549       _normals.push_back( bord.GetNorm() );
10550       if ( _normals.size() == 2 )
10551         _bothIn = !isOut( n, _normals[0], bord.GetMarkedElem() );
10552     }
10553
10554     bool IsIn( const SMDS_MeshNode* n, const SMDS_MeshElement* elem ) const
10555     {
10556       bool isIn = false;
10557       switch ( _normals.size() ) {
10558       case 1:
10559       {
10560         isIn = !isOut( n, _normals[0], elem );
10561         break;
10562       }
10563       case 2:
10564       {
10565         bool in1 = !isOut( n, _normals[0], elem );
10566         bool in2 = !isOut( n, _normals[1], elem );
10567         isIn = _bothIn ? ( in1 && in2 ) : ( in1 || in2 );
10568       }
10569       }
10570       return isIn;
10571     }
10572   };
10573
10574   //================================================================================
10575   /*!
10576    * \brief Classify an element by a plane passing through a node
10577    */
10578   //================================================================================
10579
10580   bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem )
10581   {
10582     SMESH_NodeXYZ p = n;
10583     double sumDot = 0;
10584     for ( int i = 0, nb = elem->NbCornerNodes(); i < nb; ++i )
10585     {
10586       SMESH_NodeXYZ pi = elem->GetNode( i );
10587       sumDot += norm * ( pi - p );
10588     }
10589     return sumDot < -1e-100;
10590   }
10591
10592   //================================================================================
10593   /*!
10594    * \brief Find FissureBorder's by nodes to duplicate
10595    */
10596   //================================================================================
10597
10598   void findFissureBorders( const TIDSortedElemSet&        theNodes,
10599                            std::vector< FissureBorder > & theFissureBorders )
10600   {
10601     TIDSortedElemSet::const_iterator nIt = theNodes.begin();
10602     const SMDS_MeshNode* n = dynamic_cast< const SMDS_MeshNode*>( *nIt );
10603     if ( !n ) return;
10604     SMDSAbs_ElementType elemType = SMDSAbs_Volume;
10605     if ( n->NbInverseElements( elemType ) == 0 )
10606     {
10607       elemType = SMDSAbs_Face;
10608       if ( n->NbInverseElements( elemType ) == 0 )
10609         return;
10610     }
10611     // unmark elements touching the fissure
10612     for ( ; nIt != theNodes.end(); ++nIt )
10613       SMESH_MeshAlgos::MarkElems( cast2Node(*nIt)->GetInverseElementIterator(), false );
10614
10615     // loop on elements touching the fissure to get their borders belonging to the fissure
10616     std::set< FissureBorder >              fissureBorders;
10617     std::vector< const SMDS_MeshElement* > adjElems;
10618     std::vector< const SMDS_MeshNode* >    nodes;
10619     SMDS_VolumeTool volTool;
10620     for ( nIt = theNodes.begin(); nIt != theNodes.end(); ++nIt )
10621     {
10622       SMDS_ElemIteratorPtr invIt = cast2Node(*nIt)->GetInverseElementIterator( elemType );
10623       while ( invIt->more() )
10624       {
10625         const SMDS_MeshElement* eInv = invIt->next();
10626         if ( eInv->isMarked() ) continue;
10627         eInv->setIsMarked( true );
10628
10629         if ( elemType == SMDSAbs_Volume )
10630         {
10631           volTool.Set( eInv );
10632           int iQuad = eInv->IsQuadratic() ? 2 : 1;
10633           for ( int iF = 0, nbF = volTool.NbFaces(); iF < nbF; ++iF )
10634           {
10635             const SMDS_MeshNode** nn = volTool.GetFaceNodes( iF );
10636             int                  nbN = volTool.NbFaceNodes( iF ) / iQuad;
10637             nodes.clear();
10638             bool allOnFissure = true;
10639             for ( int iN = 0; iN < nbN  && allOnFissure; iN += iQuad )
10640               if (( allOnFissure = theNodes.count( nn[ iN ])))
10641                 nodes.push_back( nn[ iN ]);
10642             if ( allOnFissure )
10643               fissureBorders.insert( std::move( FissureBorder( &nodes[0], nodes.size(),
10644                                                                elemType, adjElems )));
10645           }
10646         }
10647         else // elemType == SMDSAbs_Face
10648         {
10649           const SMDS_MeshNode* nn[2] = { eInv->GetNode( eInv->NbCornerNodes()-1 ), 0 };
10650           bool            onFissure0 = theNodes.count( nn[0] ), onFissure1;
10651           for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN; ++iN )
10652           {
10653             nn[1]      = eInv->GetNode( iN );
10654             onFissure1 = theNodes.count( nn[1] );
10655             if ( onFissure0 && onFissure1 )
10656               fissureBorders.insert( std::move( FissureBorder( nn, 2, elemType, adjElems )));
10657             nn[0]      = nn[1];
10658             onFissure0 = onFissure1;
10659           }
10660         }
10661       }
10662     }
10663
10664     theFissureBorders.reserve( theFissureBorders.size() + fissureBorders.size());
10665     std::set< FissureBorder >::iterator bord = fissureBorders.begin();
10666     for ( ; bord != fissureBorders.end(); ++bord )
10667     {
10668       theFissureBorders.push_back( std::move( const_cast<FissureBorder&>( *bord ) ));
10669     }
10670     return;
10671   } // findFissureBorders()
10672
10673   //================================================================================
10674   /*!
10675    * \brief Find elements on one side of a fissure defined by elements or nodes to duplicate
10676    *  \param [in] theElemsOrNodes - elements or nodes to duplicate
10677    *  \param [in] theNodesNot - nodes not to duplicate
10678    *  \param [out] theAffectedElems - the found elements
10679    */
10680   //================================================================================
10681
10682   void findAffectedElems( const TIDSortedElemSet& theElemsOrNodes,
10683                           TIDSortedElemSet&       theAffectedElems)
10684   {
10685     if ( theElemsOrNodes.empty() ) return;
10686
10687     // find FissureBorder's
10688
10689     std::vector< FissureBorder >           fissure;
10690     std::vector< const SMDS_MeshElement* > elemsByFacet;
10691
10692     TIDSortedElemSet::const_iterator elIt = theElemsOrNodes.begin();
10693     if ( (*elIt)->GetType() == SMDSAbs_Node )
10694     {
10695       findFissureBorders( theElemsOrNodes, fissure );
10696     }
10697     else
10698     {
10699       fissure.reserve( theElemsOrNodes.size() );
10700       for ( ; elIt != theElemsOrNodes.end(); ++elIt )
10701         fissure.push_back( std::move( FissureBorder( *elIt, elemsByFacet )));
10702     }
10703     if ( fissure.empty() )
10704       return;
10705
10706     // fill borderLinks
10707
10708     TBorderLinks borderLinks;
10709
10710     for ( size_t i = 0; i < fissure.size(); ++i )
10711     {
10712       fissure[i].AddSelfTo( borderLinks );
10713     }
10714
10715     // get theAffectedElems
10716
10717     // unmark elements having nodes on the fissure, theAffectedElems elements will be marked
10718     for ( size_t i = 0; i < fissure.size(); ++i )
10719       for ( size_t j = 0; j < fissure[i]._nodes.size(); ++j )
10720       {
10721         SMESH_MeshAlgos::MarkElemNodes( fissure[i]._nodes[j]->GetInverseElementIterator(),
10722                                         false, /*markElem=*/true );
10723       }
10724
10725     std::vector<const SMDS_MeshNode *>                 facetNodes;
10726     std::map< const SMDS_MeshNode*, FissureNormal >    fissEdgeNodes2Norm;
10727     boost::container::flat_set< const SMDS_MeshNode* > fissureNodes;
10728
10729     // choose a side of fissure
10730     fissure[0].ChooseSide();
10731     theAffectedElems.insert( fissure[0].GetMarkedElem() );
10732
10733     size_t nbCheckedBorders = 0;
10734     while ( nbCheckedBorders < fissure.size() )
10735     {
10736       // find a FissureBorder to treat
10737       FissureBorder* bord = 0;
10738       for ( size_t i = 0; i < fissure.size()  && !bord; ++i )
10739         if ( fissure[i].GetMarkedElem() )
10740           bord = & fissure[i];
10741       for ( size_t i = 0; i < fissure.size()  && !bord; ++i )
10742         if ( fissure[i].NbSub() > 0 && fissure[i]._elems[0] )
10743         {
10744           bord = & fissure[i];
10745           bord->ChooseSide();
10746           theAffectedElems.insert( bord->GetMarkedElem() );
10747         }
10748       if ( !bord ) return;
10749       ++nbCheckedBorders;
10750
10751       // treat FissureBorder's linked to bord
10752       fissureNodes.clear();
10753       fissureNodes.insert( bord->_nodes.begin(), bord->_nodes.end() );
10754       for ( size_t i = 0; i < bord->NbSub(); ++i )
10755       {
10756         TBorderLinks::iterator l2b = bord->_mappedSubs[ i ];
10757         if ( l2b == borderLinks.end() || l2b->second.empty() ) continue;
10758         std::vector< FissureBorder* >& linkedBorders = l2b->second;
10759         const SubBorder&                          sb = l2b->first;
10760         const SMDS_MeshElement*             bordElem = bord->GetMarkedElem();
10761
10762         if ( linkedBorders.size() == 1 ) // fissure edge reached, fill fissEdgeNodes2Norm
10763         {
10764           for ( int j = 0; j < sb._nbNodes; ++j )
10765             fissEdgeNodes2Norm[ sb._nodes[j] ].Add( sb._nodes[j], *bord );
10766           continue;
10767         }
10768
10769         // add to theAffectedElems elems sharing nodes of a SubBorder and a node of bordElem
10770         // until an elem adjacent to a neighbour FissureBorder is found
10771         facetNodes.clear();
10772         facetNodes.insert( facetNodes.end(), sb._nodes, sb._nodes + sb._nbNodes );
10773         facetNodes.resize( sb._nbNodes + 1 );
10774
10775         while ( bordElem )
10776         {
10777           // check if bordElem is adjacent to a neighbour FissureBorder
10778           for ( size_t j = 0; j < linkedBorders.size(); ++j )
10779           {
10780             FissureBorder* bord2 = linkedBorders[j];
10781             if ( bord2 == bord ) continue;
10782             if ( bordElem == bord2->_elems[0] || bordElem == bord2->_elems[1] )
10783               bordElem = 0;
10784             else
10785               fissureNodes.insert( bord2->_nodes.begin(), bord2->_nodes.end() );
10786           }
10787           if ( !bordElem )
10788             break;
10789
10790           // find the next bordElem
10791           const SMDS_MeshElement* nextBordElem = 0;
10792           for ( int iN = 0, nbN = bordElem->NbCornerNodes(); iN < nbN  && !nextBordElem; ++iN )
10793           {
10794             const SMDS_MeshNode* n = bordElem->GetNode( iN );
10795             if ( fissureNodes.count( n )) continue;
10796
10797             facetNodes[ sb._nbNodes ] = n;
10798             elemsByFacet.clear();
10799             if ( SMDS_Mesh::GetElementsByNodes( facetNodes, elemsByFacet ) > 1 )
10800             {
10801               for ( size_t iE = 0; iE < elemsByFacet.size(); ++iE )
10802                 if ( elemsByFacet[ iE ] != bordElem &&
10803                      !elemsByFacet[ iE ]->isMarked() )
10804                 {
10805                   theAffectedElems.insert( elemsByFacet[ iE ]);
10806                   elemsByFacet[ iE ]->setIsMarked( true );
10807                   if ( elemsByFacet[ iE ]->GetType() == bordElem->GetType() )
10808                     nextBordElem = elemsByFacet[ iE ];
10809                 }
10810             }
10811           }
10812           bordElem = nextBordElem;
10813
10814         } // while ( bordElem )
10815
10816         linkedBorders.clear(); // not to treat this link any more
10817
10818       } // loop on SubBorder's of a FissureBorder
10819
10820       bord->Clear();
10821
10822     } // loop on FissureBorder's
10823
10824
10825     // add elements sharing only one node of the fissure, except those sharing fissure edge nodes
10826
10827     // mark nodes of theAffectedElems
10828     SMESH_MeshAlgos::MarkElemNodes( theAffectedElems.begin(), theAffectedElems.end(), true );
10829
10830     // unmark nodes of the fissure
10831     elIt = theElemsOrNodes.begin();
10832     if ( (*elIt)->GetType() == SMDSAbs_Node )
10833       SMESH_MeshAlgos::MarkElems( elIt, theElemsOrNodes.end(), false );
10834     else
10835       SMESH_MeshAlgos::MarkElemNodes( elIt, theElemsOrNodes.end(), false );
10836
10837     std::vector< gp_XYZ > normVec;
10838
10839     // loop on nodes of the fissure, add elements having marked nodes
10840     for ( elIt = theElemsOrNodes.begin(); elIt != theElemsOrNodes.end(); ++elIt )
10841     {
10842       const SMDS_MeshElement* e = (*elIt);
10843       if ( e->GetType() != SMDSAbs_Node )
10844         e->setIsMarked( true ); // avoid adding a fissure element
10845
10846       for ( int iN = 0, nbN = e->NbCornerNodes(); iN < nbN; ++iN )
10847       {
10848         const SMDS_MeshNode* n = e->GetNode( iN );
10849         if ( fissEdgeNodes2Norm.count( n ))
10850           continue;
10851
10852         SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
10853         while ( invIt->more() )
10854         {
10855           const SMDS_MeshElement* eInv = invIt->next();
10856           if ( eInv->isMarked() ) continue;
10857           eInv->setIsMarked( true );
10858
10859           SMDS_ElemIteratorPtr nIt = eInv->nodesIterator();
10860           while( nIt->more() )
10861             if ( nIt->next()->isMarked())
10862             {
10863               theAffectedElems.insert( eInv );
10864               SMESH_MeshAlgos::MarkElems( eInv->nodesIterator(), true );
10865               n->setIsMarked( false );
10866               break;
10867             }
10868         }
10869       }
10870     }
10871
10872     // add elements on the fissure edge
10873     std::map< const SMDS_MeshNode*, FissureNormal >::iterator n2N;
10874     for ( n2N = fissEdgeNodes2Norm.begin(); n2N != fissEdgeNodes2Norm.end(); ++n2N )
10875     {
10876       const SMDS_MeshNode* edgeNode = n2N->first;
10877       const FissureNormal & normals = n2N->second;
10878
10879       SMDS_ElemIteratorPtr invIt = edgeNode->GetInverseElementIterator();
10880       while ( invIt->more() )
10881       {
10882         const SMDS_MeshElement* eInv = invIt->next();
10883         if ( eInv->isMarked() ) continue;
10884         eInv->setIsMarked( true );
10885
10886         // classify eInv using normals
10887         bool toAdd = normals.IsIn( edgeNode, eInv );
10888         if ( toAdd ) // check if all nodes lie on the fissure edge
10889         {
10890           bool notOnEdge = false;
10891           for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN  && !notOnEdge; ++iN )
10892             notOnEdge = !fissEdgeNodes2Norm.count( eInv->GetNode( iN ));
10893           toAdd = notOnEdge;
10894         }
10895         if ( toAdd )
10896         {
10897           theAffectedElems.insert( eInv );
10898         }
10899       }
10900     }
10901
10902     return;
10903   } // findAffectedElems()
10904 } // namespace
10905
10906 //================================================================================
10907 /*!
10908  * \brief Create elements equal (on same nodes) to given ones
10909  *  \param [in] theElements - a set of elems to duplicate. If it is empty, all
10910  *              elements of the uppest dimension are duplicated.
10911  */
10912 //================================================================================
10913
10914 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
10915 {
10916   ClearLastCreated();
10917   SMESHDS_Mesh* mesh = GetMeshDS();
10918
10919   // get an element type and an iterator over elements
10920
10921   SMDSAbs_ElementType type = SMDSAbs_All;
10922   SMDS_ElemIteratorPtr elemIt;
10923   if ( theElements.empty() )
10924   {
10925     if ( mesh->NbNodes() == 0 )
10926       return;
10927     // get most complex type
10928     SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10929       SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10930       SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10931     };
10932     for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10933       if ( mesh->GetMeshInfo().NbElements( types[i] ))
10934       {
10935         type = types[i];
10936         elemIt = mesh->elementsIterator( type );
10937         break;
10938       }
10939   }
10940   else
10941   {
10942     type = (*theElements.begin())->GetType();
10943     elemIt = SMESHUtils::elemSetIterator( theElements );
10944   }
10945
10946   // un-mark all elements to avoid duplicating just created elements
10947   SMESH_MeshAlgos::MarkElems( mesh->elementsIterator( type ), false );
10948
10949   // duplicate elements
10950
10951   ElemFeatures elemType;
10952
10953   vector< const SMDS_MeshNode* > nodes;
10954   while ( elemIt->more() )
10955   {
10956     const SMDS_MeshElement* elem = elemIt->next();
10957     if ( elem->GetType() != type || elem->isMarked() )
10958       continue;
10959
10960     elemType.Init( elem, /*basicOnly=*/false );
10961     nodes.assign( elem->begin_nodes(), elem->end_nodes() );
10962
10963     if ( const SMDS_MeshElement* newElem = AddElement( nodes, elemType ))
10964       newElem->setIsMarked( true );
10965   }
10966 }
10967
10968 //================================================================================
10969 /*!
10970   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10971   \param theElems - the list of elements (edges or faces) to be replicated
10972   The nodes for duplication could be found from these elements
10973   \param theNodesNot - list of nodes to NOT replicate
10974   \param theAffectedElems - the list of elements (cells and edges) to which the
10975   replicated nodes should be associated to.
10976   \return TRUE if operation has been completed successfully, FALSE otherwise
10977 */
10978 //================================================================================
10979
10980 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10981                                     const TIDSortedElemSet& theNodesNot,
10982                                     const TIDSortedElemSet& theAffectedElems )
10983 {
10984   ClearLastCreated();
10985
10986   if ( theElems.size() == 0 )
10987     return false;
10988
10989   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10990   if ( !aMeshDS )
10991     return false;
10992
10993   bool res = false;
10994   TNodeNodeMap anOldNodeToNewNode;
10995   // duplicate elements and nodes
10996   res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10997   // replce nodes by duplications
10998   res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10999   return res;
11000 }
11001
11002 //================================================================================
11003 /*!
11004   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11005   \param theMeshDS - mesh instance
11006   \param theElems - the elements replicated or modified (nodes should be changed)
11007   \param theNodesNot - nodes to NOT replicate
11008   \param theNodeNodeMap - relation of old node to new created node
11009   \param theIsDoubleElem - flag os to replicate element or modify
11010   \return TRUE if operation has been completed successfully, FALSE otherwise
11011 */
11012 //================================================================================
11013
11014 bool SMESH_MeshEditor::doubleNodes(SMESHDS_Mesh*           theMeshDS,
11015                                    const TIDSortedElemSet& theElems,
11016                                    const TIDSortedElemSet& theNodesNot,
11017                                    TNodeNodeMap&           theNodeNodeMap,
11018                                    const bool              theIsDoubleElem )
11019 {
11020   // iterate through element and duplicate them (by nodes duplication)
11021   bool res = false;
11022   std::vector<const SMDS_MeshNode*> newNodes;
11023   ElemFeatures elemType;
11024
11025   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11026   for ( ;  elemItr != theElems.end(); ++elemItr )
11027   {
11028     const SMDS_MeshElement* anElem = *elemItr;
11029     // if (!anElem)
11030     //   continue;
11031
11032     // duplicate nodes to duplicate element
11033     bool isDuplicate = false;
11034     newNodes.resize( anElem->NbNodes() );
11035     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
11036     int ind = 0;
11037     while ( anIter->more() )
11038     {
11039       const SMDS_MeshNode* aCurrNode = static_cast<const SMDS_MeshNode*>( anIter->next() );
11040       const SMDS_MeshNode*  aNewNode = aCurrNode;
11041       TNodeNodeMap::iterator     n2n = theNodeNodeMap.find( aCurrNode );
11042       if ( n2n != theNodeNodeMap.end() )
11043       {
11044         aNewNode = n2n->second;
11045       }
11046       else if ( theIsDoubleElem && !theNodesNot.count( aCurrNode ))
11047       {
11048         // duplicate node
11049         aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
11050         copyPosition( aCurrNode, aNewNode );
11051         theNodeNodeMap[ aCurrNode ] = aNewNode;
11052         myLastCreatedNodes.push_back( aNewNode );
11053       }
11054       isDuplicate |= (aCurrNode != aNewNode);
11055       newNodes[ ind++ ] = aNewNode;
11056     }
11057     if ( !isDuplicate )
11058       continue;
11059
11060     if ( theIsDoubleElem )
11061       AddElement( newNodes, elemType.Init( anElem, /*basicOnly=*/false ));
11062     else
11063       theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], newNodes.size() );
11064
11065     res = true;
11066   }
11067   return res;
11068 }
11069
11070 //================================================================================
11071 /*!
11072   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11073   \param theNodes - identifiers of nodes to be doubled
11074   \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
11075   nodes. If list of element identifiers is empty then nodes are doubled but
11076   they not assigned to elements
11077   \return TRUE if operation has been completed successfully, FALSE otherwise
11078 */
11079 //================================================================================
11080
11081 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
11082                                     const std::list< int >& theListOfModifiedElems )
11083 {
11084   ClearLastCreated();
11085
11086   if ( theListOfNodes.size() == 0 )
11087     return false;
11088
11089   SMESHDS_Mesh* aMeshDS = GetMeshDS();
11090   if ( !aMeshDS )
11091     return false;
11092
11093   // iterate through nodes and duplicate them
11094
11095   std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
11096
11097   std::list< int >::const_iterator aNodeIter;
11098   for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
11099   {
11100     const SMDS_MeshNode* aNode = aMeshDS->FindNode( *aNodeIter );
11101     if ( !aNode )
11102       continue;
11103
11104     // duplicate node
11105
11106     const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
11107     if ( aNewNode )
11108     {
11109       copyPosition( aNode, aNewNode );
11110       anOldNodeToNewNode[ aNode ] = aNewNode;
11111       myLastCreatedNodes.push_back( aNewNode );
11112     }
11113   }
11114
11115   // Change nodes of elements
11116
11117   std::vector<const SMDS_MeshNode*> aNodeArr;
11118
11119   std::list< int >::const_iterator anElemIter;
11120   for ( anElemIter =  theListOfModifiedElems.begin();
11121         anElemIter != theListOfModifiedElems.end();
11122         anElemIter++ )
11123   {
11124     const SMDS_MeshElement* anElem = aMeshDS->FindElement( *anElemIter );
11125     if ( !anElem )
11126       continue;
11127
11128     aNodeArr.assign( anElem->begin_nodes(), anElem->end_nodes() );
11129     for( size_t i = 0; i < aNodeArr.size(); ++i )
11130     {
11131       std::map< const SMDS_MeshNode*, const SMDS_MeshNode* >::iterator n2n =
11132         anOldNodeToNewNode.find( aNodeArr[ i ]);
11133       if ( n2n != anOldNodeToNewNode.end() )
11134         aNodeArr[ i ] = n2n->second;
11135     }
11136     aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], aNodeArr.size() );
11137   }
11138
11139   return true;
11140 }
11141
11142 namespace {
11143
11144   //================================================================================
11145   /*!
11146     \brief Check if element located inside shape
11147     \return TRUE if IN or ON shape, FALSE otherwise
11148   */
11149   //================================================================================
11150
11151   template<class Classifier>
11152   bool isInside(const SMDS_MeshElement* theElem,
11153                 Classifier&             theClassifier,
11154                 const double            theTol)
11155   {
11156     gp_XYZ centerXYZ (0, 0, 0);
11157     for ( SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator(); aNodeItr->more(); )
11158       centerXYZ += SMESH_NodeXYZ( aNodeItr->next() );
11159
11160     gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
11161     theClassifier.Perform(aPnt, theTol);
11162     TopAbs_State aState = theClassifier.State();
11163     return (aState == TopAbs_IN || aState == TopAbs_ON );
11164   }
11165
11166   //================================================================================
11167   /*!
11168    * \brief Classifier of the 3D point on the TopoDS_Face
11169    *        with interaface suitable for isInside()
11170    */
11171   //================================================================================
11172
11173   struct _FaceClassifier
11174   {
11175     Extrema_ExtPS       _extremum;
11176     BRepAdaptor_Surface _surface;
11177     TopAbs_State        _state;
11178
11179     _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
11180     {
11181       _extremum.Initialize( _surface,
11182                             _surface.FirstUParameter(), _surface.LastUParameter(),
11183                             _surface.FirstVParameter(), _surface.LastVParameter(),
11184                             _surface.Tolerance(), _surface.Tolerance() );
11185     }
11186     void Perform(const gp_Pnt& aPnt, double theTol)
11187     {
11188       theTol *= theTol;
11189       _state = TopAbs_OUT;
11190       _extremum.Perform(aPnt);
11191       if ( _extremum.IsDone() )
11192         for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
11193           _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
11194     }
11195     TopAbs_State State() const
11196     {
11197       return _state;
11198     }
11199   };
11200 }
11201
11202 //================================================================================
11203 /*!
11204   \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
11205   This method is the first step of DoubleNodeElemGroupsInRegion.
11206   \param theElems - list of groups of elements (edges or faces) to be replicated
11207   \param theNodesNot - list of groups of nodes not to replicated
11208   \param theShape - shape to detect affected elements (element which geometric center
11209          located on or inside shape). If the shape is null, detection is done on faces orientations
11210          (select elements with a gravity center on the side given by faces normals).
11211          This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
11212          The replicated nodes should be associated to affected elements.
11213   \return true
11214   \sa DoubleNodeElemGroupsInRegion()
11215 */
11216 //================================================================================
11217
11218 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
11219                                                    const TIDSortedElemSet& theNodesNot,
11220                                                    const TopoDS_Shape&     theShape,
11221                                                    TIDSortedElemSet&       theAffectedElems)
11222 {
11223   if ( theShape.IsNull() )
11224   {
11225     findAffectedElems( theElems, theAffectedElems );
11226   }
11227   else
11228   {
11229     const double aTol = Precision::Confusion();
11230     auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
11231     auto_ptr<_FaceClassifier>              aFaceClassifier;
11232     if ( theShape.ShapeType() == TopAbs_SOLID )
11233     {
11234       bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
11235       bsc3d->PerformInfinitePoint(aTol);
11236     }
11237     else if (theShape.ShapeType() == TopAbs_FACE )
11238     {
11239       aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
11240     }
11241
11242     // iterates on indicated elements and get elements by back references from their nodes
11243     TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11244     for ( ;  elemItr != theElems.end(); ++elemItr )
11245     {
11246       SMDS_MeshElement*     anElem = (SMDS_MeshElement*)*elemItr;
11247       SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11248       while ( nodeItr->more() )
11249       {
11250         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11251         if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11252           continue;
11253         SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11254         while ( backElemItr->more() )
11255         {
11256           const SMDS_MeshElement* curElem = backElemItr->next();
11257           if ( curElem && theElems.find(curElem) == theElems.end() &&
11258                ( bsc3d.get() ?
11259                  isInside( curElem, *bsc3d, aTol ) :
11260                  isInside( curElem, *aFaceClassifier, aTol )))
11261             theAffectedElems.insert( curElem );
11262         }
11263       }
11264     }
11265   }
11266   return true;
11267 }
11268
11269 //================================================================================
11270 /*!
11271   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11272   \param theElems - group of of elements (edges or faces) to be replicated
11273   \param theNodesNot - group of nodes not to replicate
11274   \param theShape - shape to detect affected elements (element which geometric center
11275   located on or inside shape).
11276   The replicated nodes should be associated to affected elements.
11277   \return TRUE if operation has been completed successfully, FALSE otherwise
11278 */
11279 //================================================================================
11280
11281 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
11282                                             const TIDSortedElemSet& theNodesNot,
11283                                             const TopoDS_Shape&     theShape )
11284 {
11285   if ( theShape.IsNull() )
11286     return false;
11287
11288   const double aTol = Precision::Confusion();
11289   SMESHUtils::Deleter< BRepClass3d_SolidClassifier> bsc3d;
11290   SMESHUtils::Deleter<_FaceClassifier>              aFaceClassifier;
11291   if ( theShape.ShapeType() == TopAbs_SOLID )
11292   {
11293     bsc3d._obj = new BRepClass3d_SolidClassifier( theShape );
11294     bsc3d->PerformInfinitePoint(aTol);
11295   }
11296   else if (theShape.ShapeType() == TopAbs_FACE )
11297   {
11298     aFaceClassifier._obj = new _FaceClassifier( TopoDS::Face( theShape ));
11299   }
11300
11301   // iterates on indicated elements and get elements by back references from their nodes
11302   TIDSortedElemSet anAffected;
11303   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11304   for ( ;  elemItr != theElems.end(); ++elemItr )
11305   {
11306     SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11307     if (!anElem)
11308       continue;
11309
11310     SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11311     while ( nodeItr->more() )
11312     {
11313       const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11314       if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11315         continue;
11316       SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11317       while ( backElemItr->more() )
11318       {
11319         const SMDS_MeshElement* curElem = backElemItr->next();
11320         if ( curElem && theElems.find(curElem) == theElems.end() &&
11321              ( bsc3d ?
11322                isInside( curElem, *bsc3d, aTol ) :
11323                isInside( curElem, *aFaceClassifier, aTol )))
11324           anAffected.insert( curElem );
11325       }
11326     }
11327   }
11328   return DoubleNodes( theElems, theNodesNot, anAffected );
11329 }
11330
11331 /*!
11332  *  \brief compute an oriented angle between two planes defined by four points.
11333  *  The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
11334  *  @param p0 base of the rotation axe
11335  *  @param p1 extremity of the rotation axe
11336  *  @param g1 belongs to the first plane
11337  *  @param g2 belongs to the second plane
11338  */
11339 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
11340 {
11341   gp_Vec vref(p0, p1);
11342   gp_Vec v1(p0, g1);
11343   gp_Vec v2(p0, g2);
11344   gp_Vec n1 = vref.Crossed(v1);
11345   gp_Vec n2 = vref.Crossed(v2);
11346   try {
11347     return n2.AngleWithRef(n1, vref);
11348   }
11349   catch ( Standard_Failure ) {
11350   }
11351   return Max( v1.Magnitude(), v2.Magnitude() );
11352 }
11353
11354 /*!
11355  * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11356  *  The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
11357  * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
11358  * Triangles are transformed into prisms, and quadrangles into hexahedrons.
11359  * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
11360  * 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.
11361  * 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.
11362  * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
11363  * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
11364  * \param theElems - list of groups of volumes, where a group of volume is a set of
11365  *        SMDS_MeshElements sorted by Id.
11366  * \param createJointElems - if TRUE, create the elements
11367  * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
11368  *        the boundary between \a theDomains and the rest mesh
11369  * \return TRUE if operation has been completed successfully, FALSE otherwise
11370  */
11371 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11372                                                      bool                                 createJointElems,
11373                                                      bool                                 onAllBoundaries)
11374 {
11375   // MESSAGE("----------------------------------------------");
11376   // MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11377   // MESSAGE("----------------------------------------------");
11378
11379   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11380   meshDS->BuildDownWardConnectivity(true);
11381   CHRONO(50);
11382   SMDS_UnstructuredGrid *grid = meshDS->GetGrid();
11383
11384   // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11385   //     build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11386   //     build the list of nodes shared by 2 or more domains, with their domain indexes
11387
11388   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11389   std::map<int,int>celldom; // cell vtkId --> domain
11390   std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains;  // oldNode --> (id domain --> id cell)
11391   std::map<int, std::map<int,int> > nodeDomains; // oldId -->  (domainId --> newId)
11392   faceDomains.clear();
11393   celldom.clear();
11394   cellDomains.clear();
11395   nodeDomains.clear();
11396   std::map<int,int> emptyMap;
11397   std::set<int> emptySet;
11398   emptyMap.clear();
11399
11400   //MESSAGE(".. Number of domains :"<<theElems.size());
11401
11402   TIDSortedElemSet theRestDomElems;
11403   const int iRestDom  = -1;
11404   const int idom0     = onAllBoundaries ? iRestDom : 0;
11405   const int nbDomains = theElems.size();
11406
11407   // Check if the domains do not share an element
11408   for (int idom = 0; idom < nbDomains-1; idom++)
11409   {
11410     //       MESSAGE("... Check of domain #" << idom);
11411     const TIDSortedElemSet& domain = theElems[idom];
11412     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11413     for (; elemItr != domain.end(); ++elemItr)
11414     {
11415       const SMDS_MeshElement* anElem = *elemItr;
11416       int idombisdeb = idom + 1 ;
11417       // check if the element belongs to a domain further in the list
11418       for ( size_t idombis = idombisdeb; idombis < theElems.size(); idombis++ )
11419       {
11420         const TIDSortedElemSet& domainbis = theElems[idombis];
11421         if ( domainbis.count( anElem ))
11422         {
11423           MESSAGE(".... Domain #" << idom);
11424           MESSAGE(".... Domain #" << idombis);
11425           throw SALOME_Exception("The domains are not disjoint.");
11426           return false ;
11427         }
11428       }
11429     }
11430   }
11431
11432   for (int idom = 0; idom < nbDomains; idom++)
11433   {
11434
11435     // --- build a map (face to duplicate --> volume to modify)
11436     //     with all the faces shared by 2 domains (group of elements)
11437     //     and corresponding volume of this domain, for each shared face.
11438     //     a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11439
11440     //MESSAGE("... Neighbors of domain #" << idom);
11441     const TIDSortedElemSet& domain = theElems[idom];
11442     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11443     for (; elemItr != domain.end(); ++elemItr)
11444     {
11445       const SMDS_MeshElement* anElem = *elemItr;
11446       if (!anElem)
11447         continue;
11448       int vtkId = anElem->GetVtkID();
11449       //MESSAGE("  vtkId " << vtkId << " smdsId " << anElem->GetID());
11450       int neighborsVtkIds[NBMAXNEIGHBORS];
11451       int downIds[NBMAXNEIGHBORS];
11452       unsigned char downTypes[NBMAXNEIGHBORS];
11453       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11454       for (int n = 0; n < nbNeighbors; n++)
11455       {
11456         int smdsId = meshDS->FromVtkToSmds(neighborsVtkIds[n]);
11457         const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11458         if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
11459         {
11460           bool ok = false;
11461           for ( size_t idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
11462           {
11463             // MESSAGE("Domain " << idombis);
11464             const TIDSortedElemSet& domainbis = theElems[idombis];
11465             if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11466           }
11467           if ( ok || onAllBoundaries ) // the characteristics of the face is stored
11468           {
11469             DownIdType face(downIds[n], downTypes[n]);
11470             if (!faceDomains[face].count(idom))
11471             {
11472               faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11473               celldom[vtkId] = idom;
11474               //MESSAGE("       cell with a border " << vtkId << " domain " << idom);
11475             }
11476             if ( !ok )
11477             {
11478               theRestDomElems.insert( elem );
11479               faceDomains[face][iRestDom] = neighborsVtkIds[n];
11480               celldom[neighborsVtkIds[n]] = iRestDom;
11481             }
11482           }
11483         }
11484       }
11485     }
11486   }
11487
11488   //MESSAGE("Number of shared faces " << faceDomains.size());
11489   std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11490
11491   // --- explore the shared faces domain by domain,
11492   //     explore the nodes of the face and see if they belong to a cell in the domain,
11493   //     which has only a node or an edge on the border (not a shared face)
11494
11495   for (int idomain = idom0; idomain < nbDomains; idomain++)
11496   {
11497     //MESSAGE("Domain " << idomain);
11498     const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
11499     itface = faceDomains.begin();
11500     for (; itface != faceDomains.end(); ++itface)
11501     {
11502       const std::map<int, int>& domvol = itface->second;
11503       if (!domvol.count(idomain))
11504         continue;
11505       DownIdType face = itface->first;
11506       //MESSAGE(" --- face " << face.cellId);
11507       std::set<int> oldNodes;
11508       oldNodes.clear();
11509       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11510       std::set<int>::iterator itn = oldNodes.begin();
11511       for (; itn != oldNodes.end(); ++itn)
11512       {
11513         int oldId = *itn;
11514         //MESSAGE("     node " << oldId);
11515         vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11516         for (int i=0; i<l.ncells; i++)
11517         {
11518           int vtkId = l.cells[i];
11519           const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->FromVtkToSmds(vtkId));
11520           if (!domain.count(anElem))
11521             continue;
11522           int vtkType = grid->GetCellType(vtkId);
11523           int downId = grid->CellIdToDownId(vtkId);
11524           if (downId < 0)
11525           {
11526             MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11527             continue; // not OK at this stage of the algorithm:
11528             //no cells created after BuildDownWardConnectivity
11529           }
11530           DownIdType aCell(downId, vtkType);
11531           cellDomains[aCell][idomain] = vtkId;
11532           celldom[vtkId] = idomain;
11533           //MESSAGE("       cell " << vtkId << " domain " << idomain);
11534         }
11535       }
11536     }
11537   }
11538
11539   // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11540   //     for each shared face, get the nodes
11541   //     for each node, for each domain of the face, create a clone of the node
11542
11543   // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11544   //     junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11545   //     the value is the ordered domain ids. (more than 4 domains not taken into account)
11546
11547   std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11548   std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11549   std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11550
11551   //MESSAGE(".. Duplication of the nodes");
11552   for (int idomain = idom0; idomain < nbDomains; idomain++)
11553   {
11554     itface = faceDomains.begin();
11555     for (; itface != faceDomains.end(); ++itface)
11556     {
11557       const std::map<int, int>& domvol = itface->second;
11558       if (!domvol.count(idomain))
11559         continue;
11560       DownIdType face = itface->first;
11561       //MESSAGE(" --- face " << face.cellId);
11562       std::set<int> oldNodes;
11563       oldNodes.clear();
11564       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11565       std::set<int>::iterator itn = oldNodes.begin();
11566       for (; itn != oldNodes.end(); ++itn)
11567       {
11568         int oldId = *itn;
11569         if (nodeDomains[oldId].empty())
11570         {
11571           nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11572           //MESSAGE("-+-+-b     oldNode " << oldId << " domain " << idomain);
11573         }
11574         std::map<int, int>::const_iterator itdom = domvol.begin();
11575         for (; itdom != domvol.end(); ++itdom)
11576         {
11577           int idom = itdom->first;
11578           //MESSAGE("         domain " << idom);
11579           if (!nodeDomains[oldId].count(idom)) // --- node to clone
11580           {
11581             if (nodeDomains[oldId].size() >= 2) // a multiple node
11582             {
11583               vector<int> orderedDoms;
11584               //MESSAGE("multiple node " << oldId);
11585               if (mutipleNodes.count(oldId))
11586                 orderedDoms = mutipleNodes[oldId];
11587               else
11588               {
11589                 map<int,int>::iterator it = nodeDomains[oldId].begin();
11590                 for (; it != nodeDomains[oldId].end(); ++it)
11591                   orderedDoms.push_back(it->first);
11592               }
11593               orderedDoms.push_back(idom); // TODO order ==> push_front or back
11594               //stringstream txt;
11595               //for (int i=0; i<orderedDoms.size(); i++)
11596               //  txt << orderedDoms[i] << " ";
11597               //MESSAGE("orderedDoms " << txt.str());
11598               mutipleNodes[oldId] = orderedDoms;
11599             }
11600             double *coords = grid->GetPoint(oldId);
11601             SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11602             copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11603             int newId = newNode->GetVtkID();
11604             nodeDomains[oldId][idom] = newId; // cloned node for other domains
11605             //MESSAGE("-+-+-c     oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11606           }
11607         }
11608       }
11609     }
11610   }
11611
11612   //MESSAGE(".. Creation of elements");
11613   for (int idomain = idom0; idomain < nbDomains; idomain++)
11614   {
11615     itface = faceDomains.begin();
11616     for (; itface != faceDomains.end(); ++itface)
11617     {
11618       std::map<int, int> domvol = itface->second;
11619       if (!domvol.count(idomain))
11620         continue;
11621       DownIdType face = itface->first;
11622       //MESSAGE(" --- face " << face.cellId);
11623       std::set<int> oldNodes;
11624       oldNodes.clear();
11625       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11626       int nbMultipleNodes = 0;
11627       std::set<int>::iterator itn = oldNodes.begin();
11628       for (; itn != oldNodes.end(); ++itn)
11629       {
11630         int oldId = *itn;
11631         if (mutipleNodes.count(oldId))
11632           nbMultipleNodes++;
11633       }
11634       if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11635       {
11636         //MESSAGE("multiple Nodes detected on a shared face");
11637         int downId = itface->first.cellId;
11638         unsigned char cellType = itface->first.cellType;
11639         // --- shared edge or shared face ?
11640         if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11641         {
11642           int nodes[3];
11643           int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11644           for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11645             if (mutipleNodes.count(nodes[i]))
11646               if (!mutipleNodesToFace.count(nodes[i]))
11647                 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11648         }
11649         else // shared face (between two volumes)
11650         {
11651           int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11652           const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11653           const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11654           for (int ie =0; ie < nbEdges; ie++)
11655           {
11656             int nodes[3];
11657             int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11658             if ( mutipleNodes.count(nodes[0]) && mutipleNodes.count( nodes[ nbNodes-1 ]))
11659             {
11660               vector<int> vn0 = mutipleNodes[nodes[0]];
11661               vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11662               vector<int> doms;
11663               for ( size_t i0 = 0; i0 < vn0.size(); i0++ )
11664                 for ( size_t i1 = 0; i1 < vn1.size(); i1++ )
11665                   if ( vn0[i0] == vn1[i1] )
11666                     doms.push_back( vn0[ i0 ]);
11667               if ( doms.size() > 2 )
11668               {
11669                 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11670                 double *coords = grid->GetPoint(nodes[0]);
11671                 gp_Pnt p0(coords[0], coords[1], coords[2]);
11672                 coords = grid->GetPoint(nodes[nbNodes - 1]);
11673                 gp_Pnt p1(coords[0], coords[1], coords[2]);
11674                 gp_Pnt gref;
11675                 int vtkVolIds[1000];  // an edge can belong to a lot of volumes
11676                 map<int, SMDS_MeshVolume*> domvol; // domain --> a volume with the edge
11677                 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11678                 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11679                 for ( size_t id = 0; id < doms.size(); id++ )
11680                 {
11681                   int idom = doms[id];
11682                   const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11683                   for ( int ivol = 0; ivol < nbvol; ivol++ )
11684                   {
11685                     int smdsId = meshDS->FromVtkToSmds(vtkVolIds[ivol]);
11686                     const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11687                     if (domain.count(elem))
11688                     {
11689                       const SMDS_MeshVolume* svol = SMDS_Mesh::DownCast<SMDS_MeshVolume>(elem);
11690                       domvol[idom] = (SMDS_MeshVolume*) svol;
11691                       //MESSAGE("  domain " << idom << " volume " << elem->GetID());
11692                       double values[3] = { 0,0,0 };
11693                       vtkIdType npts = 0;
11694                       vtkIdType* pts = 0;
11695                       grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11696                       for ( vtkIdType i = 0; i < npts; ++i )
11697                       {
11698                         double *coords = grid->GetPoint( pts[i] );
11699                         for ( int j = 0; j < 3; ++j )
11700                           values[j] += coords[j] / npts;
11701                       }
11702                       if ( id == 0 )
11703                       {
11704                         gref.SetCoord( values[0], values[1], values[2] );
11705                         angleDom[idom] = 0;
11706                       }
11707                       else
11708                       {
11709                         gp_Pnt g( values[0], values[1], values[2] );
11710                         angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11711                         //MESSAGE("  angle=" << angleDom[idom]);
11712                       }
11713                       break;
11714                     }
11715                   }
11716                 }
11717                 map<double, int> sortedDom; // sort domains by angle
11718                 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11719                   sortedDom[ia->second] = ia->first;
11720                 vector<int> vnodes;
11721                 vector<int> vdom;
11722                 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11723                 {
11724                   vdom.push_back(ib->second);
11725                   //MESSAGE("  ordered domain " << ib->second << "  angle " << ib->first);
11726                 }
11727                 for (int ino = 0; ino < nbNodes; ino++)
11728                   vnodes.push_back(nodes[ino]);
11729                 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11730               }
11731             }
11732           }
11733         }
11734       }
11735     }
11736   }
11737
11738   // --- iterate on shared faces (volumes to modify, face to extrude)
11739   //     get node id's of the face (id SMDS = id VTK)
11740   //     create flat element with old and new nodes if requested
11741
11742   // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11743   //     (domain1 X domain2) = domain1 + MAXINT*domain2
11744
11745   std::map<int, std::map<long,int> > nodeQuadDomains;
11746   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11747
11748   //MESSAGE(".. Creation of elements: simple junction");
11749   if (createJointElems)
11750   {
11751     int idg;
11752     string joints2DName = "joints2D";
11753     mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
11754     SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11755     string joints3DName = "joints3D";
11756     mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
11757     SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11758
11759     itface = faceDomains.begin();
11760     for (; itface != faceDomains.end(); ++itface)
11761     {
11762       DownIdType face = itface->first;
11763       std::set<int> oldNodes;
11764       std::set<int>::iterator itn;
11765       oldNodes.clear();
11766       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11767
11768       std::map<int, int> domvol = itface->second;
11769       std::map<int, int>::iterator itdom = domvol.begin();
11770       int dom1 = itdom->first;
11771       int vtkVolId = itdom->second;
11772       itdom++;
11773       int dom2 = itdom->first;
11774       SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11775                                                        nodeQuadDomains);
11776       stringstream grpname;
11777       grpname << "j_";
11778       if (dom1 < dom2)
11779         grpname << dom1 << "_" << dom2;
11780       else
11781         grpname << dom2 << "_" << dom1;
11782       string namegrp = grpname.str();
11783       if (!mapOfJunctionGroups.count(namegrp))
11784         mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
11785       SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11786       if (sgrp)
11787         sgrp->Add(vol->GetID());
11788       if (vol->GetType() == SMDSAbs_Volume)
11789         joints3DGrp->Add(vol->GetID());
11790       else if (vol->GetType() == SMDSAbs_Face)
11791         joints2DGrp->Add(vol->GetID());
11792     }
11793   }
11794
11795   // --- create volumes on multiple domain intersection if requested
11796   //     iterate on mutipleNodesToFace
11797   //     iterate on edgesMultiDomains
11798
11799   //MESSAGE(".. Creation of elements: multiple junction");
11800   if (createJointElems)
11801   {
11802     // --- iterate on mutipleNodesToFace
11803
11804     std::map<int, std::vector<int> >::iterator itn =  mutipleNodesToFace.begin();
11805     for (; itn != mutipleNodesToFace.end(); ++itn)
11806     {
11807       int node = itn->first;
11808       vector<int> orderDom = itn->second;
11809       vector<vtkIdType> orderedNodes;
11810       for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11811         orderedNodes.push_back( nodeDomains[ node ][ orderDom[ idom ]]);
11812       SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11813
11814       stringstream grpname;
11815       grpname << "m2j_";
11816       grpname << 0 << "_" << 0;
11817       int idg;
11818       string namegrp = grpname.str();
11819       if (!mapOfJunctionGroups.count(namegrp))
11820         mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
11821       SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11822       if (sgrp)
11823         sgrp->Add(face->GetID());
11824     }
11825
11826     // --- iterate on edgesMultiDomains
11827
11828     std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11829     for (; ite != edgesMultiDomains.end(); ++ite)
11830     {
11831       vector<int> nodes = ite->first;
11832       vector<int> orderDom = ite->second;
11833       vector<vtkIdType> orderedNodes;
11834       if (nodes.size() == 2)
11835       {
11836         //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11837         for ( size_t ino = 0; ino < nodes.size(); ino++ )
11838           if ( orderDom.size() == 3 )
11839             for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11840               orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11841           else
11842             for (int idom = orderDom.size()-1; idom >=0; idom--)
11843               orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11844         SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11845
11846         int idg;
11847         string namegrp = "jointsMultiples";
11848         if (!mapOfJunctionGroups.count(namegrp))
11849           mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11850         SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11851         if (sgrp)
11852           sgrp->Add(vol->GetID());
11853       }
11854       else
11855       {
11856         //INFOS("Quadratic multiple joints not implemented");
11857         // TODO quadratic nodes
11858       }
11859     }
11860   }
11861
11862   // --- list the explicit faces and edges of the mesh that need to be modified,
11863   //     i.e. faces and edges built with one or more duplicated nodes.
11864   //     associate these faces or edges to their corresponding domain.
11865   //     only the first domain found is kept when a face or edge is shared
11866
11867   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11868   std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11869   faceOrEdgeDom.clear();
11870   feDom.clear();
11871
11872   //MESSAGE(".. Modification of elements");
11873   for (int idomain = idom0; idomain < nbDomains; idomain++)
11874   {
11875     std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11876     for (; itnod != nodeDomains.end(); ++itnod)
11877     {
11878       int oldId = itnod->first;
11879       //MESSAGE("     node " << oldId);
11880       vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11881       for (int i = 0; i < l.ncells; i++)
11882       {
11883         int vtkId = l.cells[i];
11884         int vtkType = grid->GetCellType(vtkId);
11885         int downId = grid->CellIdToDownId(vtkId);
11886         if (downId < 0)
11887           continue; // new cells: not to be modified
11888         DownIdType aCell(downId, vtkType);
11889         int volParents[1000];
11890         int nbvol = grid->GetParentVolumes(volParents, vtkId);
11891         for (int j = 0; j < nbvol; j++)
11892           if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11893             if (!feDom.count(vtkId))
11894             {
11895               feDom[vtkId] = idomain;
11896               faceOrEdgeDom[aCell] = emptyMap;
11897               faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11898               //MESSAGE("affect cell " << this->GetMeshDS()->FromVtkToSmds(vtkId) << " domain " << idomain
11899               //        << " type " << vtkType << " downId " << downId);
11900             }
11901       }
11902     }
11903   }
11904
11905   // --- iterate on shared faces (volumes to modify, face to extrude)
11906   //     get node id's of the face
11907   //     replace old nodes by new nodes in volumes, and update inverse connectivity
11908
11909   std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11910   for (int m=0; m<3; m++)
11911   {
11912     std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11913     itface = (*amap).begin();
11914     for (; itface != (*amap).end(); ++itface)
11915     {
11916       DownIdType face = itface->first;
11917       std::set<int> oldNodes;
11918       std::set<int>::iterator itn;
11919       oldNodes.clear();
11920       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11921       //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11922       std::map<int, int> localClonedNodeIds;
11923
11924       std::map<int, int> domvol = itface->second;
11925       std::map<int, int>::iterator itdom = domvol.begin();
11926       for (; itdom != domvol.end(); ++itdom)
11927       {
11928         int idom = itdom->first;
11929         int vtkVolId = itdom->second;
11930         //MESSAGE("modify nodes of cell " << this->GetMeshDS()->FromVtkToSmds(vtkVolId) << " domain " << idom);
11931         localClonedNodeIds.clear();
11932         for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11933         {
11934           int oldId = *itn;
11935           if (nodeDomains[oldId].count(idom))
11936           {
11937             localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11938             //MESSAGE("     node " << oldId << " --> " << localClonedNodeIds[oldId]);
11939           }
11940         }
11941         meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11942       }
11943     }
11944   }
11945
11946   // Remove empty groups (issue 0022812)
11947   std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
11948   for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
11949   {
11950     if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
11951       myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
11952   }
11953
11954   meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11955   grid->DeleteLinks();
11956
11957   CHRONOSTOP(50);
11958   counters::stats();
11959   return true;
11960 }
11961
11962 /*!
11963  * \brief Double nodes on some external faces and create flat elements.
11964  * Flat elements are mainly used by some types of mechanic calculations.
11965  *
11966  * Each group of the list must be constituted of faces.
11967  * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11968  * @param theElems - list of groups of faces, where a group of faces is a set of
11969  * SMDS_MeshElements sorted by Id.
11970  * @return TRUE if operation has been completed successfully, FALSE otherwise
11971  */
11972 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11973 {
11974   // MESSAGE("-------------------------------------------------");
11975   // MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11976   // MESSAGE("-------------------------------------------------");
11977
11978   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11979
11980   // --- For each group of faces
11981   //     duplicate the nodes, create a flat element based on the face
11982   //     replace the nodes of the faces by their clones
11983
11984   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11985   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11986   clonedNodes.clear();
11987   intermediateNodes.clear();
11988   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11989   mapOfJunctionGroups.clear();
11990
11991   for ( size_t idom = 0; idom < theElems.size(); idom++ )
11992   {
11993     const TIDSortedElemSet&           domain = theElems[idom];
11994     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11995     for ( ; elemItr != domain.end(); ++elemItr )
11996     {
11997       const SMDS_MeshFace* aFace = meshDS->DownCast<SMDS_MeshFace> ( *elemItr );
11998       if (!aFace)
11999         continue;
12000       // MESSAGE("aFace=" << aFace->GetID());
12001       bool isQuad = aFace->IsQuadratic();
12002       vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
12003
12004       // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
12005
12006       SMDS_NodeIteratorPtr nodeIt = aFace->nodeIterator();
12007       while (nodeIt->more())
12008       {
12009         const SMDS_MeshNode* node = nodeIt->next();
12010         bool isMedium = ( isQuad && aFace->IsMediumNode( node ));
12011         if (isMedium)
12012           ln2.push_back(node);
12013         else
12014           ln0.push_back(node);
12015
12016         const SMDS_MeshNode* clone = 0;
12017         if (!clonedNodes.count(node))
12018         {
12019           clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
12020           copyPosition( node, clone );
12021           clonedNodes[node] = clone;
12022         }
12023         else
12024           clone = clonedNodes[node];
12025
12026         if (isMedium)
12027           ln3.push_back(clone);
12028         else
12029           ln1.push_back(clone);
12030
12031         const SMDS_MeshNode* inter = 0;
12032         if (isQuad && (!isMedium))
12033         {
12034           if (!intermediateNodes.count(node))
12035           {
12036             inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
12037             copyPosition( node, inter );
12038             intermediateNodes[node] = inter;
12039           }
12040           else
12041             inter = intermediateNodes[node];
12042           ln4.push_back(inter);
12043         }
12044       }
12045
12046       // --- extrude the face
12047
12048       vector<const SMDS_MeshNode*> ln;
12049       SMDS_MeshVolume* vol = 0;
12050       vtkIdType aType = aFace->GetVtkType();
12051       switch (aType)
12052       {
12053       case VTK_TRIANGLE:
12054         vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
12055         // MESSAGE("vol prism " << vol->GetID());
12056         ln.push_back(ln1[0]);
12057         ln.push_back(ln1[1]);
12058         ln.push_back(ln1[2]);
12059         break;
12060       case VTK_QUAD:
12061         vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
12062         // MESSAGE("vol hexa " << vol->GetID());
12063         ln.push_back(ln1[0]);
12064         ln.push_back(ln1[1]);
12065         ln.push_back(ln1[2]);
12066         ln.push_back(ln1[3]);
12067         break;
12068       case VTK_QUADRATIC_TRIANGLE:
12069         vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
12070                                 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
12071         // MESSAGE("vol quad prism " << vol->GetID());
12072         ln.push_back(ln1[0]);
12073         ln.push_back(ln1[1]);
12074         ln.push_back(ln1[2]);
12075         ln.push_back(ln3[0]);
12076         ln.push_back(ln3[1]);
12077         ln.push_back(ln3[2]);
12078         break;
12079       case VTK_QUADRATIC_QUAD:
12080         //              vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
12081         //                                      ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
12082         //                                      ln4[0], ln4[1], ln4[2], ln4[3]);
12083         vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
12084                                 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
12085                                 ln4[0], ln4[1], ln4[2], ln4[3]);
12086         // MESSAGE("vol quad hexa " << vol->GetID());
12087         ln.push_back(ln1[0]);
12088         ln.push_back(ln1[1]);
12089         ln.push_back(ln1[2]);
12090         ln.push_back(ln1[3]);
12091         ln.push_back(ln3[0]);
12092         ln.push_back(ln3[1]);
12093         ln.push_back(ln3[2]);
12094         ln.push_back(ln3[3]);
12095         break;
12096       case VTK_POLYGON:
12097         break;
12098       default:
12099         break;
12100       }
12101
12102       if (vol)
12103       {
12104         stringstream grpname;
12105         grpname << "jf_";
12106         grpname << idom;
12107         int idg;
12108         string namegrp = grpname.str();
12109         if (!mapOfJunctionGroups.count(namegrp))
12110           mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
12111         SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
12112         if (sgrp)
12113           sgrp->Add(vol->GetID());
12114       }
12115
12116       // --- modify the face
12117
12118       const_cast<SMDS_MeshFace*>( aFace )->ChangeNodes( &ln[0], ln.size() );
12119     }
12120   }
12121   return true;
12122 }
12123
12124 /*!
12125  *  \brief identify all the elements around a geom shape, get the faces delimiting the hole
12126  *  Build groups of volume to remove, groups of faces to replace on the skin of the object,
12127  *  groups of faces to remove inside the object, (idem edges).
12128  *  Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
12129  */
12130 void SMESH_MeshEditor::CreateHoleSkin(double                          radius,
12131                                       const TopoDS_Shape&             theShape,
12132                                       SMESH_NodeSearcher*             theNodeSearcher,
12133                                       const char*                     groupName,
12134                                       std::vector<double>&            nodesCoords,
12135                                       std::vector<std::vector<int> >& listOfListOfNodes)
12136 {
12137   // MESSAGE("--------------------------------");
12138   // MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
12139   // MESSAGE("--------------------------------");
12140
12141   // --- zone of volumes to remove is given :
12142   //     1 either by a geom shape (one or more vertices) and a radius,
12143   //     2 either by a group of nodes (representative of the shape)to use with the radius,
12144   //     3 either by a group of nodes where all the elements build on one of this nodes are to remove,
12145   //     In the case 2, the group of nodes is an external group of nodes from another mesh,
12146   //     In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
12147   //     defined by it's name.
12148
12149   SMESHDS_GroupBase* groupDS = 0;
12150   SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
12151   while ( groupIt->more() )
12152   {
12153     groupDS = 0;
12154     SMESH_Group * group = groupIt->next();
12155     if ( !group ) continue;
12156     groupDS = group->GetGroupDS();
12157     if ( !groupDS || groupDS->IsEmpty() ) continue;
12158     std::string grpName = group->GetName();
12159     //MESSAGE("grpName=" << grpName);
12160     if (grpName == groupName)
12161       break;
12162     else
12163       groupDS = 0;
12164   }
12165
12166   bool isNodeGroup = false;
12167   bool isNodeCoords = false;
12168   if (groupDS)
12169   {
12170     if (groupDS->GetType() != SMDSAbs_Node)
12171       return;
12172     isNodeGroup = true;     // a group of nodes exists and it is in this mesh
12173   }
12174
12175   if (nodesCoords.size() > 0)
12176     isNodeCoords = true; // a list o nodes given by their coordinates
12177   //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
12178
12179   // --- define groups to build
12180
12181   int idg; // --- group of SMDS volumes
12182   string grpvName = groupName;
12183   grpvName += "_vol";
12184   SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
12185   if (!grp)
12186   {
12187     MESSAGE("group not created " << grpvName);
12188     return;
12189   }
12190   SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
12191
12192   int idgs; // --- group of SMDS faces on the skin
12193   string grpsName = groupName;
12194   grpsName += "_skin";
12195   SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
12196   if (!grps)
12197   {
12198     MESSAGE("group not created " << grpsName);
12199     return;
12200   }
12201   SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
12202
12203   int idgi; // --- group of SMDS faces internal (several shapes)
12204   string grpiName = groupName;
12205   grpiName += "_internalFaces";
12206   SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
12207   if (!grpi)
12208   {
12209     MESSAGE("group not created " << grpiName);
12210     return;
12211   }
12212   SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
12213
12214   int idgei; // --- group of SMDS faces internal (several shapes)
12215   string grpeiName = groupName;
12216   grpeiName += "_internalEdges";
12217   SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
12218   if (!grpei)
12219   {
12220     MESSAGE("group not created " << grpeiName);
12221     return;
12222   }
12223   SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
12224
12225   // --- build downward connectivity
12226
12227   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
12228   meshDS->BuildDownWardConnectivity(true);
12229   SMDS_UnstructuredGrid* grid = meshDS->GetGrid();
12230
12231   // --- set of volumes detected inside
12232
12233   std::set<int> setOfInsideVol;
12234   std::set<int> setOfVolToCheck;
12235
12236   std::vector<gp_Pnt> gpnts;
12237   gpnts.clear();
12238
12239   if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
12240   {
12241     //MESSAGE("group of nodes provided");
12242     SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
12243     while ( elemIt->more() )
12244     {
12245       const SMDS_MeshElement* elem = elemIt->next();
12246       if (!elem)
12247         continue;
12248       const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
12249       if (!node)
12250         continue;
12251       SMDS_MeshElement* vol = 0;
12252       SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
12253       while (volItr->more())
12254       {
12255         vol = (SMDS_MeshElement*)volItr->next();
12256         setOfInsideVol.insert(vol->GetVtkID());
12257         sgrp->Add(vol->GetID());
12258       }
12259     }
12260   }
12261   else if (isNodeCoords)
12262   {
12263     //MESSAGE("list of nodes coordinates provided");
12264     size_t i = 0;
12265     int k = 0;
12266     while ( i < nodesCoords.size()-2 )
12267     {
12268       double x = nodesCoords[i++];
12269       double y = nodesCoords[i++];
12270       double z = nodesCoords[i++];
12271       gp_Pnt p = gp_Pnt(x, y ,z);
12272       gpnts.push_back(p);
12273       //MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
12274       k++;
12275     }
12276   }
12277   else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
12278   {
12279     //MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
12280     TopTools_IndexedMapOfShape vertexMap;
12281     TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
12282     gp_Pnt p = gp_Pnt(0,0,0);
12283     if (vertexMap.Extent() < 1)
12284       return;
12285
12286     for ( int i = 1; i <= vertexMap.Extent(); ++i )
12287     {
12288       const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
12289       p = BRep_Tool::Pnt(vertex);
12290       gpnts.push_back(p);
12291       //MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
12292     }
12293   }
12294
12295   if (gpnts.size() > 0)
12296   {
12297     const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
12298     //MESSAGE("startNode->nodeId " << nodeId);
12299
12300     double radius2 = radius*radius;
12301     //MESSAGE("radius2 " << radius2);
12302
12303     // --- volumes on start node
12304
12305     setOfVolToCheck.clear();
12306     SMDS_MeshElement* startVol = 0;
12307     SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
12308     while (volItr->more())
12309     {
12310       startVol = (SMDS_MeshElement*)volItr->next();
12311       setOfVolToCheck.insert(startVol->GetVtkID());
12312     }
12313     if (setOfVolToCheck.empty())
12314     {
12315       MESSAGE("No volumes found");
12316       return;
12317     }
12318
12319     // --- starting with central volumes then their neighbors, check if they are inside
12320     //     or outside the domain, until no more new neighbor volume is inside.
12321     //     Fill the group of inside volumes
12322
12323     std::map<int, double> mapOfNodeDistance2;
12324     mapOfNodeDistance2.clear();
12325     std::set<int> setOfOutsideVol;
12326     while (!setOfVolToCheck.empty())
12327     {
12328       std::set<int>::iterator it = setOfVolToCheck.begin();
12329       int vtkId = *it;
12330       //MESSAGE("volume to check,  vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12331       bool volInside = false;
12332       vtkIdType npts = 0;
12333       vtkIdType* pts = 0;
12334       grid->GetCellPoints(vtkId, npts, pts);
12335       for (int i=0; i<npts; i++)
12336       {
12337         double distance2 = 0;
12338         if (mapOfNodeDistance2.count(pts[i]))
12339         {
12340           distance2 = mapOfNodeDistance2[pts[i]];
12341           //MESSAGE("point " << pts[i] << " distance2 " << distance2);
12342         }
12343         else
12344         {
12345           double *coords = grid->GetPoint(pts[i]);
12346           gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
12347           distance2 = 1.E40;
12348           for ( size_t j = 0; j < gpnts.size(); j++ )
12349           {
12350             double d2 = aPoint.SquareDistance( gpnts[ j ]);
12351             if (d2 < distance2)
12352             {
12353               distance2 = d2;
12354               if (distance2 < radius2)
12355                 break;
12356             }
12357           }
12358           mapOfNodeDistance2[pts[i]] = distance2;
12359           //MESSAGE("  point "  << pts[i]  << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " <<  coords[2]);
12360         }
12361         if (distance2 < radius2)
12362         {
12363           volInside = true; // one or more nodes inside the domain
12364           sgrp->Add(meshDS->FromVtkToSmds(vtkId));
12365           break;
12366         }
12367       }
12368       if (volInside)
12369       {
12370         setOfInsideVol.insert(vtkId);
12371         //MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12372         int neighborsVtkIds[NBMAXNEIGHBORS];
12373         int downIds[NBMAXNEIGHBORS];
12374         unsigned char downTypes[NBMAXNEIGHBORS];
12375         int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12376         for (int n = 0; n < nbNeighbors; n++)
12377           if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12378             setOfVolToCheck.insert(neighborsVtkIds[n]);
12379       }
12380       else
12381       {
12382         setOfOutsideVol.insert(vtkId);
12383         //MESSAGE("  volume outside, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12384       }
12385       setOfVolToCheck.erase(vtkId);
12386     }
12387   }
12388
12389   // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12390   //     If yes, add the volume to the inside set
12391
12392   bool addedInside = true;
12393   std::set<int> setOfVolToReCheck;
12394   while (addedInside)
12395   {
12396     //MESSAGE(" --------------------------- re check");
12397     addedInside = false;
12398     std::set<int>::iterator itv = setOfInsideVol.begin();
12399     for (; itv != setOfInsideVol.end(); ++itv)
12400     {
12401       int vtkId = *itv;
12402       int neighborsVtkIds[NBMAXNEIGHBORS];
12403       int downIds[NBMAXNEIGHBORS];
12404       unsigned char downTypes[NBMAXNEIGHBORS];
12405       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12406       for (int n = 0; n < nbNeighbors; n++)
12407         if (!setOfInsideVol.count(neighborsVtkIds[n]))
12408           setOfVolToReCheck.insert(neighborsVtkIds[n]);
12409     }
12410     setOfVolToCheck = setOfVolToReCheck;
12411     setOfVolToReCheck.clear();
12412     while  (!setOfVolToCheck.empty())
12413     {
12414       std::set<int>::iterator it = setOfVolToCheck.begin();
12415       int vtkId = *it;
12416       if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12417       {
12418         //MESSAGE("volume to recheck,  vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12419         int countInside = 0;
12420         int neighborsVtkIds[NBMAXNEIGHBORS];
12421         int downIds[NBMAXNEIGHBORS];
12422         unsigned char downTypes[NBMAXNEIGHBORS];
12423         int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12424         for (int n = 0; n < nbNeighbors; n++)
12425           if (setOfInsideVol.count(neighborsVtkIds[n]))
12426             countInside++;
12427         //MESSAGE("countInside " << countInside);
12428         if (countInside > 1)
12429         {
12430           //MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12431           setOfInsideVol.insert(vtkId);
12432           sgrp->Add(meshDS->FromVtkToSmds(vtkId));
12433           addedInside = true;
12434         }
12435         else
12436           setOfVolToReCheck.insert(vtkId);
12437       }
12438       setOfVolToCheck.erase(vtkId);
12439     }
12440   }
12441
12442   // --- map of Downward faces at the boundary, inside the global volume
12443   //     map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12444   //     fill group of SMDS faces inside the volume (when several volume shapes)
12445   //     fill group of SMDS faces on the skin of the global volume (if skin)
12446
12447   std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12448   std::map<DownIdType, int, DownIdCompare> skinFaces;     // faces on the skin of the global volume --> corresponding cell
12449   std::set<int>::iterator it = setOfInsideVol.begin();
12450   for (; it != setOfInsideVol.end(); ++it)
12451   {
12452     int vtkId = *it;
12453     //MESSAGE("  vtkId " << vtkId  << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12454     int neighborsVtkIds[NBMAXNEIGHBORS];
12455     int downIds[NBMAXNEIGHBORS];
12456     unsigned char downTypes[NBMAXNEIGHBORS];
12457     int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12458     for (int n = 0; n < nbNeighbors; n++)
12459     {
12460       int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12461       if (neighborDim == 3)
12462       {
12463         if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12464         {
12465           DownIdType face(downIds[n], downTypes[n]);
12466           boundaryFaces[face] = vtkId;
12467         }
12468         // if the face between to volumes is in the mesh, get it (internal face between shapes)
12469         int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12470         if (vtkFaceId >= 0)
12471         {
12472           sgrpi->Add(meshDS->FromVtkToSmds(vtkFaceId));
12473           // find also the smds edges on this face
12474           int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12475           const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12476           const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12477           for (int i = 0; i < nbEdges; i++)
12478           {
12479             int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12480             if (vtkEdgeId >= 0)
12481               sgrpei->Add(meshDS->FromVtkToSmds(vtkEdgeId));
12482           }
12483         }
12484       }
12485       else if (neighborDim == 2) // skin of the volume
12486       {
12487         DownIdType face(downIds[n], downTypes[n]);
12488         skinFaces[face] = vtkId;
12489         int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12490         if (vtkFaceId >= 0)
12491           sgrps->Add(meshDS->FromVtkToSmds(vtkFaceId));
12492       }
12493     }
12494   }
12495
12496   // --- identify the edges constituting the wire of each subshape on the skin
12497   //     define polylines with the nodes of edges, equivalent to wires
12498   //     project polylines on subshapes, and partition, to get geom faces
12499
12500   std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12501   std::set<int> emptySet;
12502   emptySet.clear();
12503   std::set<int> shapeIds;
12504
12505   SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12506   while (itelem->more())
12507   {
12508     const SMDS_MeshElement *elem = itelem->next();
12509     int shapeId = elem->getshapeId();
12510     int   vtkId = elem->GetVtkID();
12511     if (!shapeIdToVtkIdSet.count(shapeId))
12512     {
12513       shapeIdToVtkIdSet[shapeId] = emptySet;
12514       shapeIds.insert(shapeId);
12515     }
12516     shapeIdToVtkIdSet[shapeId].insert(vtkId);
12517   }
12518
12519   std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12520   std::set<DownIdType, DownIdCompare> emptyEdges;
12521   emptyEdges.clear();
12522
12523   std::map<int, std::set<int> >::iterator itShape =  shapeIdToVtkIdSet.begin();
12524   for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12525   {
12526     int shapeId = itShape->first;
12527     //MESSAGE(" --- Shape ID --- "<< shapeId);
12528     shapeIdToEdges[shapeId] = emptyEdges;
12529
12530     std::vector<int> nodesEdges;
12531
12532     std::set<int>::iterator its = itShape->second.begin();
12533     for (; its != itShape->second.end(); ++its)
12534     {
12535       int vtkId = *its;
12536       //MESSAGE("     " << vtkId);
12537       int neighborsVtkIds[NBMAXNEIGHBORS];
12538       int downIds[NBMAXNEIGHBORS];
12539       unsigned char downTypes[NBMAXNEIGHBORS];
12540       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12541       for (int n = 0; n < nbNeighbors; n++)
12542       {
12543         if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12544           continue;
12545         int smdsId = meshDS->FromVtkToSmds(neighborsVtkIds[n]);
12546         const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12547         if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12548         {
12549           DownIdType edge(downIds[n], downTypes[n]);
12550           if (!shapeIdToEdges[shapeId].count(edge))
12551           {
12552             shapeIdToEdges[shapeId].insert(edge);
12553             int vtkNodeId[3];
12554             int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12555             nodesEdges.push_back(vtkNodeId[0]);
12556             nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12557             //MESSAGE("       --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12558           }
12559         }
12560       }
12561     }
12562
12563     std::list<int> order;
12564     order.clear();
12565     if (nodesEdges.size() > 0)
12566     {
12567       order.push_back(nodesEdges[0]); //MESSAGE("       --- back " << order.back()+1); // SMDS id = VTK id + 1;
12568       nodesEdges[0] = -1;
12569       order.push_back(nodesEdges[1]); //MESSAGE("       --- back " << order.back()+1);
12570       nodesEdges[1] = -1; // do not reuse this edge
12571       bool found = true;
12572       while (found)
12573       {
12574         int nodeTofind = order.back(); // try first to push back
12575         int i = 0;
12576         for ( i = 0; i < (int)nodesEdges.size(); i++ )
12577           if (nodesEdges[i] == nodeTofind)
12578             break;
12579         if ( i == (int) nodesEdges.size() )
12580           found = false; // no follower found on back
12581         else
12582         {
12583           if (i%2) // odd ==> use the previous one
12584             if (nodesEdges[i-1] < 0)
12585               found = false;
12586             else
12587             {
12588               order.push_back(nodesEdges[i-1]); //MESSAGE("       --- back " << order.back()+1);
12589               nodesEdges[i-1] = -1;
12590             }
12591           else // even ==> use the next one
12592             if (nodesEdges[i+1] < 0)
12593               found = false;
12594             else
12595             {
12596               order.push_back(nodesEdges[i+1]); //MESSAGE("       --- back " << order.back()+1);
12597               nodesEdges[i+1] = -1;
12598             }
12599         }
12600         if (found)
12601           continue;
12602         // try to push front
12603         found = true;
12604         nodeTofind = order.front(); // try to push front
12605         for ( i = 0;  i < (int)nodesEdges.size(); i++ )
12606           if ( nodesEdges[i] == nodeTofind )
12607             break;
12608         if ( i == (int)nodesEdges.size() )
12609         {
12610           found = false; // no predecessor found on front
12611           continue;
12612         }
12613         if (i%2) // odd ==> use the previous one
12614           if (nodesEdges[i-1] < 0)
12615             found = false;
12616           else
12617           {
12618             order.push_front(nodesEdges[i-1]); //MESSAGE("       --- front " << order.front()+1);
12619             nodesEdges[i-1] = -1;
12620           }
12621         else // even ==> use the next one
12622           if (nodesEdges[i+1] < 0)
12623             found = false;
12624           else
12625           {
12626             order.push_front(nodesEdges[i+1]); //MESSAGE("       --- front " << order.front()+1);
12627             nodesEdges[i+1] = -1;
12628           }
12629       }
12630     }
12631
12632
12633     std::vector<int> nodes;
12634     nodes.push_back(shapeId);
12635     std::list<int>::iterator itl = order.begin();
12636     for (; itl != order.end(); itl++)
12637     {
12638       nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12639       //MESSAGE("              ordered node " << nodes[nodes.size()-1]);
12640     }
12641     listOfListOfNodes.push_back(nodes);
12642   }
12643
12644   //     partition geom faces with blocFissure
12645   //     mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12646   //     mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12647
12648   return;
12649 }
12650
12651
12652 //================================================================================
12653 /*!
12654  * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12655  * The created 2D mesh elements based on nodes of free faces of boundary volumes
12656  * \return TRUE if operation has been completed successfully, FALSE otherwise
12657  */
12658 //================================================================================
12659
12660 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12661 {
12662   // iterates on volume elements and detect all free faces on them
12663   SMESHDS_Mesh* aMesh = GetMeshDS();
12664   if (!aMesh)
12665     return false;
12666
12667   ElemFeatures faceType( SMDSAbs_Face );
12668   int nbFree = 0, nbExisted = 0, nbCreated = 0;
12669   SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12670   while(vIt->more())
12671   {
12672     const SMDS_MeshVolume* volume = vIt->next();
12673     SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12674     vTool.SetExternalNormal();
12675     const int iQuad = volume->IsQuadratic();
12676     faceType.SetQuad( iQuad );
12677     for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12678     {
12679       if (!vTool.IsFreeFace(iface))
12680         continue;
12681       nbFree++;
12682       vector<const SMDS_MeshNode *> nodes;
12683       int nbFaceNodes = vTool.NbFaceNodes(iface);
12684       const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12685       int inode = 0;
12686       for ( ; inode < nbFaceNodes; inode += iQuad+1)
12687         nodes.push_back(faceNodes[inode]);
12688
12689       if (iQuad) // add medium nodes
12690       {
12691         for ( inode = 1; inode < nbFaceNodes; inode += 2)
12692           nodes.push_back(faceNodes[inode]);
12693         if ( nbFaceNodes == 9 ) // bi-quadratic quad
12694           nodes.push_back(faceNodes[8]);
12695       }
12696       // add new face based on volume nodes
12697       if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) )
12698       {
12699         nbExisted++; // face already exsist
12700       }
12701       else
12702       {
12703         AddElement( nodes, faceType.SetPoly( nbFaceNodes/(iQuad+1) > 4 ));
12704         nbCreated++;
12705       }
12706     }
12707   }
12708   return ( nbFree == ( nbExisted + nbCreated ));
12709 }
12710
12711 namespace
12712 {
12713   inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12714   {
12715     if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12716       return n;
12717     return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12718   }
12719 }
12720 //================================================================================
12721 /*!
12722  * \brief Creates missing boundary elements
12723  *  \param elements - elements whose boundary is to be checked
12724  *  \param dimension - defines type of boundary elements to create
12725  *  \param group - a group to store created boundary elements in
12726  *  \param targetMesh - a mesh to store created boundary elements in
12727  *  \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12728  *  \param toCopyExistingBoundary - if true, not only new but also pre-existing
12729  *                                boundary elements will be copied into the targetMesh
12730  *  \param toAddExistingBondary - if true, not only new but also pre-existing
12731  *                                boundary elements will be added into the new group
12732  *  \param aroundElements - if true, elements will be created on boundary of given
12733  *                          elements else, on boundary of the whole mesh.
12734  * \return nb of added boundary elements
12735  */
12736 //================================================================================
12737
12738 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12739                                        Bnd_Dimension           dimension,
12740                                        SMESH_Group*            group/*=0*/,
12741                                        SMESH_Mesh*             targetMesh/*=0*/,
12742                                        bool                    toCopyElements/*=false*/,
12743                                        bool                    toCopyExistingBoundary/*=false*/,
12744                                        bool                    toAddExistingBondary/*= false*/,
12745                                        bool                    aroundElements/*= false*/)
12746 {
12747   SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12748   SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12749   // hope that all elements are of the same type, do not check them all
12750   if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12751     throw SALOME_Exception(LOCALIZED("wrong element type"));
12752
12753   if ( !targetMesh )
12754     toCopyElements = toCopyExistingBoundary = false;
12755
12756   SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12757   SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12758   int nbAddedBnd = 0;
12759
12760   // editor adding present bnd elements and optionally holding elements to add to the group
12761   SMESH_MeshEditor* presentEditor;
12762   SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12763   presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12764
12765   SMESH_MesherHelper helper( *myMesh );
12766   const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12767   SMDS_VolumeTool vTool;
12768   TIDSortedElemSet avoidSet;
12769   const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12770   size_t inode;
12771
12772   typedef vector<const SMDS_MeshNode*> TConnectivity;
12773   TConnectivity tgtNodes;
12774   ElemFeatures elemKind( missType ), elemToCopy;
12775
12776   vector<const SMDS_MeshElement*> presentBndElems;
12777   vector<TConnectivity>           missingBndElems;
12778   vector<int>                     freeFacets;
12779   TConnectivity nodes, elemNodes;
12780
12781   SMDS_ElemIteratorPtr eIt;
12782   if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12783   else                  eIt = SMESHUtils::elemSetIterator( elements );
12784
12785   while ( eIt->more() )
12786   {
12787     const SMDS_MeshElement* elem = eIt->next();
12788     const int              iQuad = elem->IsQuadratic();
12789     elemKind.SetQuad( iQuad );
12790
12791     // ------------------------------------------------------------------------------------
12792     // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12793     // ------------------------------------------------------------------------------------
12794     presentBndElems.clear();
12795     missingBndElems.clear();
12796     freeFacets.clear(); nodes.clear(); elemNodes.clear();
12797     if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12798     {
12799       const SMDS_MeshElement* otherVol = 0;
12800       for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12801       {
12802         if ( !vTool.IsFreeFace(iface, &otherVol) &&
12803              ( !aroundElements || elements.count( otherVol )))
12804           continue;
12805         freeFacets.push_back( iface );
12806       }
12807       if ( missType == SMDSAbs_Face )
12808         vTool.SetExternalNormal();
12809       for ( size_t i = 0; i < freeFacets.size(); ++i )
12810       {
12811         int                iface = freeFacets[i];
12812         const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12813         const size_t nbFaceNodes = vTool.NbFaceNodes (iface);
12814         if ( missType == SMDSAbs_Edge ) // boundary edges
12815         {
12816           nodes.resize( 2+iQuad );
12817           for ( size_t i = 0; i < nbFaceNodes; i += 1+iQuad )
12818           {
12819             for ( size_t j = 0; j < nodes.size(); ++j )
12820               nodes[ j ] = nn[ i+j ];
12821             if ( const SMDS_MeshElement* edge =
12822                  aMesh->FindElement( nodes, SMDSAbs_Edge, /*noMedium=*/false ))
12823               presentBndElems.push_back( edge );
12824             else
12825               missingBndElems.push_back( nodes );
12826           }
12827         }
12828         else // boundary face
12829         {
12830           nodes.clear();
12831           for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12832             nodes.push_back( nn[inode] ); // add corner nodes
12833           if (iQuad)
12834             for ( inode = 1; inode < nbFaceNodes; inode += 2)
12835               nodes.push_back( nn[inode] ); // add medium nodes
12836           int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12837           if ( iCenter > 0 )
12838             nodes.push_back( vTool.GetNodes()[ iCenter ] );
12839
12840           if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12841                                                                SMDSAbs_Face, /*noMedium=*/false ))
12842             presentBndElems.push_back( f );
12843           else
12844             missingBndElems.push_back( nodes );
12845
12846           if ( targetMesh != myMesh )
12847           {
12848             // add 1D elements on face boundary to be added to a new mesh
12849             const SMDS_MeshElement* edge;
12850             for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12851             {
12852               if ( iQuad )
12853                 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12854               else
12855                 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12856               if ( edge && avoidSet.insert( edge ).second )
12857                 presentBndElems.push_back( edge );
12858             }
12859           }
12860         }
12861       }
12862     }
12863     else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12864     {
12865       avoidSet.clear(), avoidSet.insert( elem );
12866       elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesIterator() ),
12867                         SMDS_MeshElement::iterator() );
12868       elemNodes.push_back( elemNodes[0] );
12869       nodes.resize( 2 + iQuad );
12870       const int nbLinks = elem->NbCornerNodes();
12871       for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12872       {
12873         nodes[0] = elemNodes[iN];
12874         nodes[1] = elemNodes[iN+1+iQuad];
12875         if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12876           continue; // not free link
12877
12878         if ( iQuad ) nodes[2] = elemNodes[iN+1];
12879         if ( const SMDS_MeshElement* edge =
12880              aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12881           presentBndElems.push_back( edge );
12882         else
12883           missingBndElems.push_back( nodes );
12884       }
12885     }
12886
12887     // ---------------------------------
12888     // 2. Add missing boundary elements
12889     // ---------------------------------
12890     if ( targetMesh != myMesh )
12891       // instead of making a map of nodes in this mesh and targetMesh,
12892       // we create nodes with same IDs.
12893       for ( size_t i = 0; i < missingBndElems.size(); ++i )
12894       {
12895         TConnectivity& srcNodes = missingBndElems[i];
12896         tgtNodes.resize( srcNodes.size() );
12897         for ( inode = 0; inode < srcNodes.size(); ++inode )
12898           tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12899         if ( /*aroundElements && */tgtEditor.GetMeshDS()->FindElement( tgtNodes,
12900                                                                    missType,
12901                                                                    /*noMedium=*/false))
12902           continue;
12903         tgtEditor.AddElement( tgtNodes, elemKind.SetPoly( tgtNodes.size()/(iQuad+1) > 4 ));
12904         ++nbAddedBnd;
12905       }
12906     else
12907       for ( size_t i = 0; i < missingBndElems.size(); ++i )
12908       {
12909         TConnectivity& nodes = missingBndElems[ i ];
12910         if ( /*aroundElements && */tgtEditor.GetMeshDS()->FindElement( nodes,
12911                                                                    missType,
12912                                                                    /*noMedium=*/false))
12913           continue;
12914         SMDS_MeshElement* newElem =
12915           tgtEditor.AddElement( nodes, elemKind.SetPoly( nodes.size()/(iQuad+1) > 4 ));
12916         nbAddedBnd += bool( newElem );
12917
12918         // try to set a new element to a shape
12919         if ( myMesh->HasShapeToMesh() )
12920         {
12921           bool ok = true;
12922           set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12923           const size_t nbN = nodes.size() / (iQuad+1 );
12924           for ( inode = 0; inode < nbN && ok; ++inode )
12925           {
12926             pair<int, TopAbs_ShapeEnum> i_stype =
12927               helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12928             if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12929               mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12930           }
12931           if ( ok && mediumShapes.size() > 1 )
12932           {
12933             set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12934             pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12935             for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12936             {
12937               if (( ok = ( stype_i->first != stype_i_0.first )))
12938                 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12939                                         aMesh->IndexToShape( stype_i_0.second ));
12940             }
12941           }
12942           if ( ok && mediumShapes.begin()->first == missShapeType )
12943             aMesh->SetMeshElementOnShape( newElem, mediumShapes.begin()->second );
12944         }
12945       }
12946
12947     // ----------------------------------
12948     // 3. Copy present boundary elements
12949     // ----------------------------------
12950     if ( toCopyExistingBoundary )
12951       for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12952       {
12953         const SMDS_MeshElement* e = presentBndElems[i];
12954         tgtNodes.resize( e->NbNodes() );
12955         for ( inode = 0; inode < tgtNodes.size(); ++inode )
12956           tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12957         presentEditor->AddElement( tgtNodes, elemToCopy.Init( e ));
12958       }
12959     else // store present elements to add them to a group
12960       for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12961       {
12962         presentEditor->myLastCreatedElems.push_back( presentBndElems[ i ]);
12963       }
12964
12965   } // loop on given elements
12966
12967   // ---------------------------------------------
12968   // 4. Fill group with boundary elements
12969   // ---------------------------------------------
12970   if ( group )
12971   {
12972     if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12973       for ( size_t i = 0; i < tgtEditor.myLastCreatedElems.size(); ++i )
12974         g->SMDSGroup().Add( tgtEditor.myLastCreatedElems[ i ]);
12975   }
12976   tgtEditor.myLastCreatedElems.clear();
12977   tgtEditor2.myLastCreatedElems.clear();
12978
12979   // -----------------------
12980   // 5. Copy given elements
12981   // -----------------------
12982   if ( toCopyElements && targetMesh != myMesh )
12983   {
12984     if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12985     else                  eIt = SMESHUtils::elemSetIterator( elements );
12986     while (eIt->more())
12987     {
12988       const SMDS_MeshElement* elem = eIt->next();
12989       tgtNodes.resize( elem->NbNodes() );
12990       for ( inode = 0; inode < tgtNodes.size(); ++inode )
12991         tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12992       tgtEditor.AddElement( tgtNodes, elemToCopy.Init( elem ));
12993
12994       tgtEditor.myLastCreatedElems.clear();
12995     }
12996   }
12997   return nbAddedBnd;
12998 }
12999
13000 //================================================================================
13001 /*!
13002  * \brief Copy node position and set \a to node on the same geometry
13003  */
13004 //================================================================================
13005
13006 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
13007                                      const SMDS_MeshNode* to )
13008 {
13009   if ( !from || !to ) return;
13010
13011   SMDS_PositionPtr pos = from->GetPosition();
13012   if ( !pos || from->getshapeId() < 1 ) return;
13013
13014   switch ( pos->GetTypeOfPosition() )
13015   {
13016   case SMDS_TOP_3DSPACE: break;
13017
13018   case SMDS_TOP_FACE:
13019   {
13020     SMDS_FacePositionPtr fPos = pos;
13021     GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
13022                                 fPos->GetUParameter(), fPos->GetVParameter() );
13023     break;
13024   }
13025   case SMDS_TOP_EDGE:
13026   {
13027     // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
13028     SMDS_EdgePositionPtr ePos = pos;
13029     GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
13030     break;
13031   }
13032   case SMDS_TOP_VERTEX:
13033   {
13034     GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
13035     break;
13036   }
13037   case SMDS_TOP_UNSPEC:
13038   default:;
13039   }
13040 }
13041
13042 namespace // utils for MakePolyLine
13043 {
13044   //================================================================================
13045   /*!
13046    * \brief Sequence of found points and a current point data
13047    */
13048   struct Path
13049   {
13050     std::vector< gp_XYZ >   myPoints;
13051     double                  myLength;
13052
13053     int                     mySrcPntInd; //!< start point index
13054     const SMDS_MeshElement* myFace;
13055     SMESH_NodeXYZ           myNode1;
13056     SMESH_NodeXYZ           myNode2;
13057     int                     myNodeInd1;
13058     int                     myNodeInd2;
13059     double                  myDot1;
13060     double                  myDot2;
13061     TIDSortedElemSet        myElemSet, myAvoidSet;
13062
13063     Path(): myLength(0.0), myFace(0) {}
13064
13065     bool SetCutAtCorner( const SMESH_NodeXYZ&    cornerNode,
13066                          const SMDS_MeshElement* face,
13067                          const gp_XYZ&           plnNorm,
13068                          const gp_XYZ&           plnOrig );
13069
13070     void AddPoint( const gp_XYZ& p );
13071
13072     bool Extend( const gp_XYZ& plnNorm, const gp_XYZ& plnOrig );
13073
13074     bool ReachSamePoint( const Path& other );
13075
13076     static void Remove( std::vector< Path > & paths, size_t& i );
13077   };
13078
13079   //================================================================================
13080   /*!
13081    * \brief Return true if this Path meats another
13082    */
13083   //================================================================================
13084
13085   bool Path::ReachSamePoint( const Path& other )
13086   {
13087     return ( mySrcPntInd != other.mySrcPntInd &&
13088              myFace == other.myFace );
13089   }
13090
13091   //================================================================================
13092   /*!
13093    * \brief Remove a path from a vector
13094    */
13095   //================================================================================
13096
13097   void Path::Remove( std::vector< Path > & paths, size_t& i )
13098   {
13099     if ( paths.size() > 1 )
13100     {
13101       size_t j = paths.size() - 1; // last item to be removed
13102       if ( i < j )
13103       {
13104         paths[ i ].myPoints.swap( paths[ j ].myPoints );
13105         paths[ i ].myLength    = paths[ j ].myLength;
13106         paths[ i ].mySrcPntInd = paths[ j ].mySrcPntInd;
13107         paths[ i ].myFace      = paths[ j ].myFace;
13108         paths[ i ].myNode1     = paths[ j ].myNode1;
13109         paths[ i ].myNode2     = paths[ j ].myNode2;
13110         paths[ i ].myNodeInd1  = paths[ j ].myNodeInd1;
13111         paths[ i ].myNodeInd2  = paths[ j ].myNodeInd2;
13112         paths[ i ].myDot1      = paths[ j ].myDot1;
13113         paths[ i ].myDot2      = paths[ j ].myDot2;
13114       }
13115     }
13116     paths.pop_back();
13117     if ( i > 0 )
13118       --i;
13119   }
13120
13121   //================================================================================
13122   /*!
13123    * \brief Store a point that is at a node of a face if the face is intersected by plane.
13124    *        Return false if the node is a sole intersection point of the face and the plane
13125    */
13126   //================================================================================
13127
13128   bool Path::SetCutAtCorner( const SMESH_NodeXYZ&    cornerNode,
13129                              const SMDS_MeshElement* face,
13130                              const gp_XYZ&           plnNorm,
13131                              const gp_XYZ&           plnOrig )
13132   {
13133     if ( face == myFace )
13134       return false;
13135     myNodeInd1 = face->GetNodeIndex( cornerNode._node );
13136     myNodeInd2 = ( myNodeInd1 + 1 ) % face->NbCornerNodes();
13137     int   ind3 = ( myNodeInd1 + 2 ) % face->NbCornerNodes();
13138     myNode1.Set( face->GetNode( ind3 ));
13139     myNode2.Set( face->GetNode( myNodeInd2 ));
13140
13141     myDot1 = plnNorm * ( myNode1 - plnOrig );
13142     myDot2 = plnNorm * ( myNode2 - plnOrig );
13143
13144     bool ok = ( myDot1 * myDot2 < 0 );
13145     if ( !ok && myDot1 * myDot2 == 0 )
13146     {
13147       ok = ( myDot1 != myDot2 );
13148       if ( ok && myFace )
13149         ok = ( myFace->GetNodeIndex(( myDot1 == 0 ? myNode1 : myNode2 )._node ) < 0 );
13150     }
13151     if ( ok )
13152     {
13153       myFace = face;
13154       myDot1 = 0;
13155       AddPoint( cornerNode );
13156     }
13157     return ok;
13158   }
13159
13160   //================================================================================
13161   /*!
13162    * \brief Store a point and update myLength
13163    */
13164   //================================================================================
13165
13166   void Path::AddPoint( const gp_XYZ& p )
13167   {
13168     if ( !myPoints.empty() )
13169       myLength += ( p - myPoints.back() ).Modulus();
13170     else
13171       myLength = 0;
13172     myPoints.push_back( p );
13173   }
13174
13175   //================================================================================
13176   /*!
13177    * \brief Try to find the next point
13178    *  \param [in] plnNorm - cutting plane normal
13179    *  \param [in] plnOrig - cutting plane origin
13180    */
13181   //================================================================================
13182
13183   bool Path::Extend( const gp_XYZ& plnNorm, const gp_XYZ& plnOrig )
13184   {
13185     int nodeInd3 = ( myNodeInd1 + 1 ) % myFace->NbCornerNodes();
13186     if ( myNodeInd2 == nodeInd3 )
13187       nodeInd3 = ( myNodeInd1 + 2 ) % myFace->NbCornerNodes();
13188
13189     SMESH_NodeXYZ node3 = myFace->GetNode( nodeInd3 );
13190     double         dot3 = plnNorm * ( node3 - plnOrig );
13191
13192     if ( dot3 * myDot1 < 0. )
13193     {
13194       myNode2    = node3;
13195       myNodeInd2 = nodeInd3;
13196       myDot2     = dot3;
13197     }
13198     else if ( dot3 * myDot2 < 0. )
13199     {
13200       myNode1    = node3;
13201       myNodeInd1 = nodeInd3;
13202       myDot1     = dot3;
13203     }
13204     else if ( dot3 == 0. )
13205     {
13206       SMDS_ElemIteratorPtr fIt = node3._node->GetInverseElementIterator(SMDSAbs_Face);
13207       while ( fIt->more() )
13208         if ( SetCutAtCorner( node3, fIt->next(), plnNorm, plnOrig ))
13209           return true;
13210       return false;
13211     }
13212     else if ( myDot2 == 0. )
13213     {
13214       SMESH_NodeXYZ node2 = myNode2; // copy as myNode2 changes in SetCutAtCorner()
13215       SMDS_ElemIteratorPtr fIt = node2._node->GetInverseElementIterator(SMDSAbs_Face);
13216       while ( fIt->more() )
13217         if ( SetCutAtCorner( node2, fIt->next(), plnNorm, plnOrig ))
13218           return true;
13219       return false;
13220     }
13221
13222     double r = Abs( myDot1 / ( myDot2 - myDot1 ));
13223     AddPoint( myNode1 * ( 1 - r ) + myNode2 * r );
13224
13225     myAvoidSet.clear();
13226     myAvoidSet.insert( myFace );
13227     myFace = SMESH_MeshAlgos::FindFaceInSet( myNode1._node, myNode2._node,
13228                                              myElemSet,   myAvoidSet,
13229                                              &myNodeInd1, &myNodeInd2 );
13230     return myFace;
13231   }
13232
13233   //================================================================================
13234   /*!
13235    * \brief Compute a path between two points of PolySegment
13236    */
13237   struct PolyPathCompute
13238   {
13239     SMESH_MeshEditor::TListOfPolySegments& mySegments; //!< inout PolySegment's
13240     std::vector< Path >&                   myPaths;    //!< path of each of segments to compute
13241     SMESH_Mesh*                            myMesh;
13242     mutable std::vector< std::string >     myErrors;
13243
13244     PolyPathCompute( SMESH_MeshEditor::TListOfPolySegments& theSegments,
13245                      std::vector< Path >&                   thePaths,
13246                      SMESH_Mesh*                            theMesh):
13247       mySegments( theSegments ),
13248       myPaths( thePaths ),
13249       myMesh( theMesh ),
13250       myErrors( theSegments.size() )
13251     {
13252     }
13253 #undef SMESH_CAUGHT
13254 #define SMESH_CAUGHT myErrors[i] =
13255     void operator() ( const int i ) const
13256     {
13257       SMESH_TRY;
13258       const_cast< PolyPathCompute* >( this )->Compute( i );
13259       SMESH_CATCH( SMESH::returnError );
13260     }
13261 #undef SMESH_CAUGHT
13262     //================================================================================
13263     /*!
13264      * \brief Compute a path of a given segment
13265      */
13266     //================================================================================
13267
13268     void Compute( const int iSeg )
13269     {
13270       SMESH_MeshEditor::PolySegment& polySeg = mySegments[ iSeg ];
13271
13272       // get a cutting plane
13273
13274       gp_XYZ p1 = SMESH_NodeXYZ( polySeg.myNode1[0] );
13275       gp_XYZ p2 = SMESH_NodeXYZ( polySeg.myNode1[1] );
13276       if ( polySeg.myNode2[0] ) p1 = 0.5 * ( p1 + SMESH_NodeXYZ( polySeg.myNode2[0] ));
13277       if ( polySeg.myNode2[1] ) p2 = 0.5 * ( p2 + SMESH_NodeXYZ( polySeg.myNode2[1] ));
13278
13279       gp_XYZ plnNorm = ( p1 - p2 ) ^ polySeg.myVector.XYZ();
13280       gp_XYZ plnOrig = p2;
13281
13282       // find paths connecting the 2 end points of polySeg
13283
13284       std::vector< Path > paths; paths.reserve(10);
13285
13286       // initialize paths
13287
13288       for ( int iP = 0; iP < 2; ++iP ) // loop on the polySeg end points
13289       {
13290         Path path;
13291         path.mySrcPntInd = iP;
13292         size_t nbPaths = paths.size();
13293
13294         if ( polySeg.myNode2[ iP ] && polySeg.myNode2[ iP ] != polySeg.myNode1[ iP ] )
13295         {
13296           while (( path.myFace = SMESH_MeshAlgos::FindFaceInSet( polySeg.myNode1[ iP ],
13297                                                                  polySeg.myNode2[ iP ],
13298                                                                  path.myElemSet,
13299                                                                  path.myAvoidSet,
13300                                                                  &path.myNodeInd1,
13301                                                                  &path.myNodeInd2 )))
13302           {
13303             path.myNode1.Set( polySeg.myNode1[ iP ]);
13304             path.myNode2.Set( polySeg.myNode2[ iP ]);
13305             path.myDot1 = plnNorm * ( path.myNode1 - plnOrig );
13306             path.myDot2 = plnNorm * ( path.myNode2 - plnOrig );
13307             path.myPoints.clear();
13308             path.AddPoint( 0.5 * ( path.myNode1 + path.myNode2 ));
13309             path.myAvoidSet.insert( path.myFace );
13310             paths.push_back( path );
13311           }
13312           if ( nbPaths == paths.size() )
13313             throw SALOME_Exception ( SMESH_Comment("No face edge found by point ") << iP+1
13314                                      << " in a PolySegment " << iSeg );
13315         }
13316         else // an end point is at node
13317         {
13318           std::set<const SMDS_MeshNode* > nodes;
13319           SMDS_ElemIteratorPtr fIt = polySeg.myNode1[ iP ]->GetInverseElementIterator(SMDSAbs_Face);
13320           while ( fIt->more() )
13321           {
13322             path.myPoints.clear();
13323             if ( path.SetCutAtCorner( polySeg.myNode1[ iP ], fIt->next(), plnNorm, plnOrig ))
13324             {
13325               if (( path.myDot1 * path.myDot2 != 0 ) ||
13326                   ( nodes.insert( path.myDot1 == 0 ? path.myNode1._node : path.myNode2._node ).second ))
13327                 paths.push_back( path );
13328             }
13329           }
13330         }
13331
13332         // look for a one-segment path
13333         for ( size_t i = 0; i < nbPaths; ++i )
13334           for ( size_t j = nbPaths; j < paths.size(); ++j )
13335             if ( paths[i].myFace == paths[j].myFace )
13336             {
13337               myPaths[ iSeg ].myPoints.push_back( paths[i].myPoints[0] );
13338               myPaths[ iSeg ].myPoints.push_back( paths[j].myPoints[0] );
13339               paths.clear();
13340             }
13341       }
13342
13343       // extend paths
13344
13345       myPaths[ iSeg ].myLength = 1e100;
13346
13347       while ( paths.size() >= 2 )
13348       {
13349         for ( size_t i = 0; i < paths.size(); ++i )
13350         {
13351           Path& path = paths[ i ];
13352           if ( !path.Extend( plnNorm, plnOrig ) ||         // path reached a mesh boundary
13353                path.myLength > myPaths[ iSeg ].myLength ) // path is longer than others
13354           {
13355             Path::Remove( paths, i );
13356             continue;
13357           }
13358
13359           // join paths that reach same point
13360           for ( size_t j = 0; j < paths.size(); ++j )
13361           {
13362             if ( i != j && paths[i].ReachSamePoint( paths[j] ))
13363             {
13364               double   distLast = ( paths[i].myPoints.back() - paths[j].myPoints.back() ).Modulus();
13365               double fullLength = ( paths[i].myLength + paths[j].myLength + distLast );
13366               if ( fullLength < myPaths[ iSeg ].myLength )
13367               {
13368                 myPaths[ iSeg ].myLength = fullLength;
13369                 std::vector< gp_XYZ > & allPoints = myPaths[ iSeg ].myPoints;
13370                 allPoints.swap( paths[i].myPoints );
13371                 allPoints.insert( allPoints.end(),
13372                                   paths[j].myPoints.rbegin(),
13373                                   paths[j].myPoints.rend() );
13374               }
13375               Path::Remove( paths, i );
13376               Path::Remove( paths, j );
13377             }
13378           }
13379         }
13380         if ( !paths.empty() && (int) paths[0].myPoints.size() > myMesh->NbFaces() )
13381           throw SALOME_Exception(LOCALIZED( "Infinite loop in MakePolyLine()"));
13382       }
13383
13384       if ( myPaths[ iSeg ].myPoints.empty() )
13385         throw SALOME_Exception( SMESH_Comment("Can't find a full path for PolySegment #") << iSeg );
13386
13387     } // PolyPathCompute::Compute()
13388
13389   }; // struct PolyPathCompute
13390
13391 } // namespace
13392
13393 //=======================================================================
13394 //function : MakePolyLine
13395 //purpose  : Create a polyline consisting of 1D mesh elements each lying on a 2D element of
13396 //           the initial mesh
13397 //=======================================================================
13398
13399 void SMESH_MeshEditor::MakePolyLine( TListOfPolySegments&   theSegments,
13400                                      SMESHDS_Group*         theGroup,
13401                                      SMESH_ElementSearcher* theSearcher)
13402 {
13403   std::vector< Path > segPaths( theSegments.size() ); // path of each of segments
13404
13405   SMESH_ElementSearcher* searcher = theSearcher;
13406   SMESHUtils::Deleter<SMESH_ElementSearcher> delSearcher;
13407   if ( !searcher )
13408   {
13409     searcher = SMESH_MeshAlgos::GetElementSearcher( *GetMeshDS() );
13410     delSearcher._obj = searcher;
13411   }
13412
13413   // get cutting planes
13414
13415   std::vector< bool > isVectorOK( theSegments.size(), true );
13416   const double planarCoef = 0.333; // plane height in planar case
13417
13418   for ( size_t iSeg = 0; iSeg < theSegments.size(); ++iSeg )
13419   {
13420     PolySegment& polySeg = theSegments[ iSeg ];
13421
13422     gp_XYZ p1 = SMESH_NodeXYZ( polySeg.myNode1[0] );
13423     gp_XYZ p2 = SMESH_NodeXYZ( polySeg.myNode1[1] );
13424     if ( polySeg.myNode2[0] ) p1 = 0.5 * ( p1 + SMESH_NodeXYZ( polySeg.myNode2[0] ));
13425     if ( polySeg.myNode2[1] ) p2 = 0.5 * ( p2 + SMESH_NodeXYZ( polySeg.myNode2[1] ));
13426
13427     gp_XYZ plnNorm = ( p1 - p2 ) ^ polySeg.myVector.XYZ();
13428
13429     isVectorOK[ iSeg ] = ( plnNorm.Modulus() > std::numeric_limits<double>::min() );
13430     if ( !isVectorOK[ iSeg ])
13431     {
13432       gp_XYZ pMid = 0.5 * ( p1 + p2 );
13433       const SMDS_MeshElement* face;
13434       polySeg.myMidProjPoint = searcher->Project( pMid, SMDSAbs_Face, &face );
13435       polySeg.myVector       = polySeg.myMidProjPoint.XYZ() - pMid;
13436
13437       gp_XYZ faceNorm;
13438       SMESH_MeshAlgos::FaceNormal( face, faceNorm );
13439
13440       if ( polySeg.myVector.Magnitude() < Precision::Confusion() ||
13441            polySeg.myVector * faceNorm  < Precision::Confusion() )
13442       {
13443         polySeg.myVector = faceNorm;
13444         polySeg.myMidProjPoint = pMid + faceNorm * ( p1 - p2 ).Modulus() * planarCoef;
13445       }
13446     }
13447     else
13448     {
13449       polySeg.myVector = plnNorm ^ ( p1 - p2 );
13450     }
13451   }
13452
13453   // assure that inverse elements are constructed, avoid their concurrent building in threads
13454   GetMeshDS()->nodesIterator()->next()->NbInverseElements();
13455
13456   // find paths
13457
13458   PolyPathCompute algo( theSegments, segPaths, myMesh );
13459   OSD_Parallel::For( 0, theSegments.size(), algo, theSegments.size() == 1 );
13460
13461   for ( size_t iSeg = 0; iSeg < theSegments.size(); ++iSeg )
13462     if ( !algo.myErrors[ iSeg ].empty() )
13463       throw SALOME_Exception( algo.myErrors[ iSeg ].c_str() );
13464
13465   // create an 1D mesh
13466
13467   const SMDS_MeshNode *n, *nPrev = 0;
13468   SMESHDS_Mesh* mesh = GetMeshDS();
13469
13470   for ( size_t iSeg = 0; iSeg < theSegments.size(); ++iSeg )
13471   {
13472     const Path& path = segPaths[iSeg];
13473     if ( path.myPoints.size() < 2 )
13474       continue;
13475
13476     double tol = path.myLength / path.myPoints.size() / 1000.;
13477     if ( !nPrev || ( SMESH_NodeXYZ( nPrev ) - path.myPoints[0] ).SquareModulus() > tol*tol )
13478     {
13479       nPrev = mesh->AddNode( path.myPoints[0].X(), path.myPoints[0].Y(), path.myPoints[0].Z() );
13480       myLastCreatedNodes.push_back( nPrev );
13481     }
13482     for ( size_t iP = 1; iP < path.myPoints.size(); ++iP )
13483     {
13484       n = mesh->AddNode( path.myPoints[iP].X(), path.myPoints[iP].Y(), path.myPoints[iP].Z() );
13485       myLastCreatedNodes.push_back( n );
13486
13487       const SMDS_MeshElement* elem = mesh->AddEdge( nPrev, n );
13488       myLastCreatedElems.push_back( elem );
13489       if ( theGroup )
13490         theGroup->Add( elem );
13491
13492       nPrev = n;
13493     }
13494
13495     // return a vector
13496
13497     gp_XYZ pMid = 0.5 * ( path.myPoints[0] + path.myPoints.back() );
13498     if ( isVectorOK[ iSeg ])
13499     {
13500       // find the most distance point of a path
13501       double maxDist = 0;
13502       for ( size_t iP = 1; iP < path.myPoints.size(); ++iP )
13503       {
13504         double dist = Abs( theSegments[iSeg].myVector * ( path.myPoints[iP] - path.myPoints[0] ));
13505         if ( dist > maxDist )
13506         {
13507           maxDist = dist;
13508           theSegments[iSeg].myMidProjPoint = path.myPoints[iP];
13509         }
13510       }
13511       if ( maxDist < Precision::Confusion() ) // planar case
13512         theSegments[iSeg].myMidProjPoint =
13513           pMid + theSegments[iSeg].myVector.XYZ().Normalized() * path.myLength * planarCoef;
13514     }
13515     theSegments[iSeg].myVector = gp_Vec( pMid, theSegments[iSeg].myMidProjPoint );
13516   }
13517
13518   return;
13519 }