Salome HOME
Merge branch 'V8_4_BR'
[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_VtkVolume* >( 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     return false;
687
688   const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( theTria1 );
689   if (!F1) return false;
690   const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( theTria2 );
691   if (!F2) return false;
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_TNodeXYZ( nodes[3] ) +
809               SMESH_TNodeXYZ( nodes[4] ) +
810               SMESH_TNodeXYZ( 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   const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
890   if (!F1) return false;
891   const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
892   if (!F2) return false;
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   const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
1010   if (!F1) return false;
1011   const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
1012   if (!F2) return false;
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     {
1029       aMesh->SetMeshElementOnShape( newElem, aShapeId );
1030     }
1031     aMesh->RemoveElement( tr1 );
1032     aMesh->RemoveElement( tr2 );
1033
1034     return true;
1035   }
1036
1037   // check case of quadratic faces
1038   if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
1039     return false;
1040   if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
1041     return false;
1042
1043   //       5
1044   //  1 +--+--+ 2  tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
1045   //    |    /|    tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
1046   //    |   / |
1047   //  7 +  +  + 6
1048   //    | /9  |
1049   //    |/    |
1050   //  4 +--+--+ 3
1051   //       8
1052
1053   vector< const SMDS_MeshNode* > N1;
1054   vector< const SMDS_MeshNode* > N2;
1055   if(!getNodesFromTwoTria(tr1,tr2,N1,N2))
1056     return false;
1057   // now we receive following N1 and N2 (using numeration as above image)
1058   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
1059   // i.e. first nodes from both arrays determ new diagonal
1060
1061   const SMDS_MeshNode* aNodes[8];
1062   aNodes[0] = N1[0];
1063   aNodes[1] = N1[1];
1064   aNodes[2] = N2[0];
1065   aNodes[3] = N2[1];
1066   aNodes[4] = N1[3];
1067   aNodes[5] = N2[5];
1068   aNodes[6] = N2[3];
1069   aNodes[7] = N1[5];
1070
1071   const SMDS_MeshElement* newElem = 0;
1072   newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
1073                             aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
1074   myLastCreatedElems.push_back(newElem);
1075   AddToSameGroups( newElem, tr1, aMesh );
1076   int aShapeId = tr1->getshapeId();
1077   if ( aShapeId )
1078   {
1079     aMesh->SetMeshElementOnShape( newElem, aShapeId );
1080   }
1081   aMesh->RemoveElement( tr1 );
1082   aMesh->RemoveElement( tr2 );
1083
1084   // remove middle node (9)
1085   GetMeshDS()->RemoveNode( N1[4] );
1086
1087   return true;
1088 }
1089
1090 //=======================================================================
1091 //function : Reorient
1092 //purpose  : Reverse theElement orientation
1093 //=======================================================================
1094
1095 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
1096 {
1097   ClearLastCreated();
1098
1099   if (!theElem)
1100     return false;
1101   SMDS_ElemIteratorPtr it = theElem->nodesIterator();
1102   if ( !it || !it->more() )
1103     return false;
1104
1105   const SMDSAbs_ElementType type = theElem->GetType();
1106   if ( type < SMDSAbs_Edge || type > SMDSAbs_Volume )
1107     return false;
1108
1109   const SMDSAbs_EntityType geomType = theElem->GetEntityType();
1110   if ( geomType == SMDSEntity_Polyhedra ) // polyhedron
1111   {
1112     const SMDS_VtkVolume* aPolyedre =
1113       dynamic_cast<const SMDS_VtkVolume*>( theElem );
1114     if (!aPolyedre) {
1115       MESSAGE("Warning: bad volumic element");
1116       return false;
1117     }
1118     const int nbFaces = aPolyedre->NbFaces();
1119     vector<const SMDS_MeshNode *> poly_nodes;
1120     vector<int> quantities (nbFaces);
1121
1122     // reverse each face of the polyedre
1123     for (int iface = 1; iface <= nbFaces; iface++) {
1124       int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
1125       quantities[iface - 1] = nbFaceNodes;
1126
1127       for (inode = nbFaceNodes; inode >= 1; inode--) {
1128         const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
1129         poly_nodes.push_back(curNode);
1130       }
1131     }
1132     return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
1133   }
1134   else // other elements
1135   {
1136     vector<const SMDS_MeshNode*> nodes( theElem->begin_nodes(), theElem->end_nodes() );
1137     const std::vector<int>& interlace = SMDS_MeshCell::reverseSmdsOrder( geomType, nodes.size() );
1138     if ( interlace.empty() )
1139     {
1140       std::reverse( nodes.begin(), nodes.end() ); // obsolete, just in case
1141     }
1142     else
1143     {
1144       SMDS_MeshCell::applyInterlace( interlace, nodes );
1145     }
1146     return GetMeshDS()->ChangeElementNodes( theElem, &nodes[0], nodes.size() );
1147   }
1148   return false;
1149 }
1150
1151 //================================================================================
1152 /*!
1153  * \brief Reorient faces.
1154  * \param theFaces - the faces to reorient. If empty the whole mesh is meant
1155  * \param theDirection - desired direction of normal of \a theFace
1156  * \param theFace - one of \a theFaces that should be oriented according to
1157  *        \a theDirection and whose orientation defines orientation of other faces
1158  * \return number of reoriented faces.
1159  */
1160 //================================================================================
1161
1162 int SMESH_MeshEditor::Reorient2D (TIDSortedElemSet &       theFaces,
1163                                   const gp_Dir&            theDirection,
1164                                   const SMDS_MeshElement * theFace)
1165 {
1166   int nbReori = 0;
1167   if ( !theFace || theFace->GetType() != SMDSAbs_Face ) return nbReori;
1168
1169   if ( theFaces.empty() )
1170   {
1171     SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator(/*idInceasingOrder=*/true);
1172     while ( fIt->more() )
1173       theFaces.insert( theFaces.end(), fIt->next() );
1174   }
1175
1176   // orient theFace according to theDirection
1177   gp_XYZ normal;
1178   SMESH_MeshAlgos::FaceNormal( theFace, normal, /*normalized=*/false );
1179   if ( normal * theDirection.XYZ() < 0 )
1180     nbReori += Reorient( theFace );
1181
1182   // Orient other faces
1183
1184   set< const SMDS_MeshElement* > startFaces, visitedFaces;
1185   TIDSortedElemSet avoidSet;
1186   set< SMESH_TLink > checkedLinks;
1187   pair< set< SMESH_TLink >::iterator, bool > linkIt_isNew;
1188
1189   if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces
1190     theFaces.erase( theFace );
1191   startFaces.insert( theFace );
1192
1193   int nodeInd1, nodeInd2;
1194   const SMDS_MeshElement*           otherFace;
1195   vector< const SMDS_MeshElement* > facesNearLink;
1196   vector< std::pair< int, int > >   nodeIndsOfFace;
1197
1198   set< const SMDS_MeshElement* >::iterator startFace = startFaces.begin();
1199   while ( !startFaces.empty() )
1200   {
1201     startFace = startFaces.begin();
1202     theFace = *startFace;
1203     startFaces.erase( startFace );
1204     if ( !visitedFaces.insert( theFace ).second )
1205       continue;
1206
1207     avoidSet.clear();
1208     avoidSet.insert(theFace);
1209
1210     NLink link( theFace->GetNode( 0 ), (SMDS_MeshNode *) 0 );
1211
1212     const int nbNodes = theFace->NbCornerNodes();
1213     for ( int i = 0; i < nbNodes; ++i ) // loop on links of theFace
1214     {
1215       link.second = theFace->GetNode(( i+1 ) % nbNodes );
1216       linkIt_isNew = checkedLinks.insert( link );
1217       if ( !linkIt_isNew.second )
1218       {
1219         // link has already been checked and won't be encountered more
1220         // if the group (theFaces) is manifold
1221         //checkedLinks.erase( linkIt_isNew.first );
1222       }
1223       else
1224       {
1225         facesNearLink.clear();
1226         nodeIndsOfFace.clear();
1227         while (( otherFace = SMESH_MeshAlgos::FindFaceInSet( link.first, link.second,
1228                                                              theFaces, avoidSet,
1229                                                              &nodeInd1, &nodeInd2 )))
1230           if ( otherFace != theFace)
1231           {
1232             facesNearLink.push_back( otherFace );
1233             nodeIndsOfFace.push_back( make_pair( nodeInd1, nodeInd2 ));
1234             avoidSet.insert( otherFace );
1235           }
1236         if ( facesNearLink.size() > 1 )
1237         {
1238           // NON-MANIFOLD mesh shell !
1239           // select a face most co-directed with theFace,
1240           // other faces won't be visited this time
1241           gp_XYZ NF, NOF;
1242           SMESH_MeshAlgos::FaceNormal( theFace, NF, /*normalized=*/false );
1243           double proj, maxProj = -1;
1244           for ( size_t i = 0; i < facesNearLink.size(); ++i ) {
1245             SMESH_MeshAlgos::FaceNormal( facesNearLink[i], NOF, /*normalized=*/false );
1246             if (( proj = Abs( NF * NOF )) > maxProj ) {
1247               maxProj = proj;
1248               otherFace = facesNearLink[i];
1249               nodeInd1  = nodeIndsOfFace[i].first;
1250               nodeInd2  = nodeIndsOfFace[i].second;
1251             }
1252           }
1253           // not to visit rejected faces
1254           for ( size_t i = 0; i < facesNearLink.size(); ++i )
1255             if ( facesNearLink[i] != otherFace && theFaces.size() > 1 )
1256               visitedFaces.insert( facesNearLink[i] );
1257         }
1258         else if ( facesNearLink.size() == 1 )
1259         {
1260           otherFace = facesNearLink[0];
1261           nodeInd1  = nodeIndsOfFace.back().first;
1262           nodeInd2  = nodeIndsOfFace.back().second;
1263         }
1264         if ( otherFace && otherFace != theFace)
1265         {
1266           // link must be reverse in otherFace if orientation to otherFace
1267           // is same as that of theFace
1268           if ( abs(nodeInd2-nodeInd1) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1269           {
1270             nbReori += Reorient( otherFace );
1271           }
1272           startFaces.insert( otherFace );
1273         }
1274       }
1275       std::swap( link.first, link.second ); // reverse the link
1276     }
1277   }
1278   return nbReori;
1279 }
1280
1281 //================================================================================
1282 /*!
1283  * \brief Reorient faces basing on orientation of adjacent volumes.
1284  * \param theFaces - faces to reorient. If empty, all mesh faces are treated.
1285  * \param theVolumes - reference volumes.
1286  * \param theOutsideNormal - to orient faces to have their normal
1287  *        pointing either \a outside or \a inside the adjacent volumes.
1288  * \return number of reoriented faces.
1289  */
1290 //================================================================================
1291
1292 int SMESH_MeshEditor::Reorient2DBy3D (TIDSortedElemSet & theFaces,
1293                                       TIDSortedElemSet & theVolumes,
1294                                       const bool         theOutsideNormal)
1295 {
1296   int nbReori = 0;
1297
1298   SMDS_ElemIteratorPtr faceIt;
1299   if ( theFaces.empty() )
1300     faceIt = GetMeshDS()->elementsIterator( SMDSAbs_Face );
1301   else
1302     faceIt = SMESHUtils::elemSetIterator( theFaces );
1303
1304   vector< const SMDS_MeshNode* > faceNodes;
1305   TIDSortedElemSet checkedVolumes;
1306   set< const SMDS_MeshNode* > faceNodesSet;
1307   SMDS_VolumeTool volumeTool;
1308
1309   while ( faceIt->more() ) // loop on given faces
1310   {
1311     const SMDS_MeshElement* face = faceIt->next();
1312     if ( face->GetType() != SMDSAbs_Face )
1313       continue;
1314
1315     const size_t nbCornersNodes = face->NbCornerNodes();
1316     faceNodes.assign( face->begin_nodes(), face->end_nodes() );
1317
1318     checkedVolumes.clear();
1319     SMDS_ElemIteratorPtr vIt = faceNodes[ 0 ]->GetInverseElementIterator( SMDSAbs_Volume );
1320     while ( vIt->more() )
1321     {
1322       const SMDS_MeshElement* volume = vIt->next();
1323
1324       if ( !checkedVolumes.insert( volume ).second )
1325         continue;
1326       if ( !theVolumes.empty() && !theVolumes.count( volume ))
1327         continue;
1328
1329       // is volume adjacent?
1330       bool allNodesCommon = true;
1331       for ( size_t iN = 1; iN < nbCornersNodes && allNodesCommon; ++iN )
1332         allNodesCommon = ( volume->GetNodeIndex( faceNodes[ iN ]) > -1 );
1333       if ( !allNodesCommon )
1334         continue;
1335
1336       // get nodes of a corresponding volume facet
1337       faceNodesSet.clear();
1338       faceNodesSet.insert( faceNodes.begin(), faceNodes.end() );
1339       volumeTool.Set( volume );
1340       int facetID = volumeTool.GetFaceIndex( faceNodesSet );
1341       if ( facetID < 0 ) continue;
1342       volumeTool.SetExternalNormal();
1343       const SMDS_MeshNode** facetNodes = volumeTool.GetFaceNodes( facetID );
1344
1345       // compare order of faceNodes and facetNodes
1346       const int iQ = 1 + ( nbCornersNodes < faceNodes.size() );
1347       int iNN[2];
1348       for ( int i = 0; i < 2; ++i )
1349       {
1350         const SMDS_MeshNode* n = facetNodes[ i*iQ ];
1351         for ( size_t iN = 0; iN < nbCornersNodes; ++iN )
1352           if ( faceNodes[ iN ] == n )
1353           {
1354             iNN[ i ] = iN;
1355             break;
1356           }
1357       }
1358       bool isOutside = Abs( iNN[0]-iNN[1] ) == 1 ? iNN[0] < iNN[1] : iNN[0] > iNN[1];
1359       if ( isOutside != theOutsideNormal )
1360         nbReori += Reorient( face );
1361     }
1362   }  // loop on given faces
1363
1364   return nbReori;
1365 }
1366
1367 //=======================================================================
1368 //function : getBadRate
1369 //purpose  :
1370 //=======================================================================
1371
1372 static double getBadRate (const SMDS_MeshElement*               theElem,
1373                           SMESH::Controls::NumericalFunctorPtr& theCrit)
1374 {
1375   SMESH::Controls::TSequenceOfXYZ P;
1376   if ( !theElem || !theCrit->GetPoints( theElem, P ))
1377     return 1e100;
1378   return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1379   //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1380 }
1381
1382 //=======================================================================
1383 //function : QuadToTri
1384 //purpose  : Cut quadrangles into triangles.
1385 //           theCrit is used to select a diagonal to cut
1386 //=======================================================================
1387
1388 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet &                   theElems,
1389                                   SMESH::Controls::NumericalFunctorPtr theCrit)
1390 {
1391   ClearLastCreated();
1392
1393   if ( !theCrit.get() )
1394     return false;
1395
1396   SMESHDS_Mesh *       aMesh = GetMeshDS();
1397   Handle(Geom_Surface) surface;
1398   SMESH_MesherHelper   helper( *GetMesh() );
1399
1400   myLastCreatedElems.reserve( theElems.size() * 2 );
1401
1402   TIDSortedElemSet::iterator itElem;
1403   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
1404   {
1405     const SMDS_MeshElement* elem = *itElem;
1406     if ( !elem || elem->GetType() != SMDSAbs_Face )
1407       continue;
1408     if ( elem->NbCornerNodes() != 4 )
1409       continue;
1410
1411     // retrieve element nodes
1412     vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1413
1414     // compare two sets of possible triangles
1415     double aBadRate1, aBadRate2; // to what extent a set is bad
1416     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1417     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1418     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1419
1420     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1421     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1422     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1423
1424     const int aShapeId = FindShape( elem );
1425     const SMDS_MeshElement* newElem1 = 0;
1426     const SMDS_MeshElement* newElem2 = 0;
1427
1428     if ( !elem->IsQuadratic() ) // split liner quadrangle
1429     {
1430       // for MaxElementLength2D functor we return minimum diagonal for splitting,
1431       // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1432       if ( aBadRate1 <= aBadRate2 ) {
1433         // tr1 + tr2 is better
1434         newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1435         newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1436       }
1437       else {
1438         // tr3 + tr4 is better
1439         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1440         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1441       }
1442     }
1443     else // split quadratic quadrangle
1444     {
1445       helper.SetIsQuadratic( true );
1446       helper.SetIsBiQuadratic( aNodes.size() == 9 );
1447
1448       helper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem ));
1449       if ( aNodes.size() == 9 )
1450       {
1451         helper.SetIsBiQuadratic( true );
1452         if ( aBadRate1 <= aBadRate2 )
1453           helper.AddTLinkNode( aNodes[0], aNodes[2], aNodes[8] );
1454         else
1455           helper.AddTLinkNode( aNodes[1], aNodes[3], aNodes[8] );
1456       }
1457       // create a new element
1458       if ( aBadRate1 <= aBadRate2 ) {
1459         newElem1 = helper.AddFace( aNodes[2], aNodes[3], aNodes[0] );
1460         newElem2 = helper.AddFace( aNodes[2], aNodes[0], aNodes[1] );
1461       }
1462       else {
1463         newElem1 = helper.AddFace( aNodes[3], aNodes[0], aNodes[1] );
1464         newElem2 = helper.AddFace( aNodes[3], aNodes[1], aNodes[2] );
1465       }
1466     } // quadratic case
1467
1468     // care of a new element
1469
1470     myLastCreatedElems.push_back(newElem1);
1471     myLastCreatedElems.push_back(newElem2);
1472     AddToSameGroups( newElem1, elem, aMesh );
1473     AddToSameGroups( newElem2, elem, aMesh );
1474
1475     // put a new triangle on the same shape
1476     if ( aShapeId )
1477       aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1478     aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1479
1480     aMesh->RemoveElement( elem );
1481   }
1482   return true;
1483 }
1484
1485 //=======================================================================
1486 /*!
1487  * \brief Split each of given quadrangles into 4 triangles.
1488  * \param theElems - The faces to be split. If empty all faces are split.
1489  */
1490 //=======================================================================
1491
1492 void SMESH_MeshEditor::QuadTo4Tri (TIDSortedElemSet & theElems)
1493 {
1494   ClearLastCreated();
1495   myLastCreatedElems.reserve( theElems.size() * 4 );
1496
1497   SMESH_MesherHelper helper( *GetMesh() );
1498   helper.SetElementsOnShape( true );
1499
1500   SMDS_ElemIteratorPtr faceIt;
1501   if ( theElems.empty() ) faceIt = GetMeshDS()->elementsIterator(SMDSAbs_Face);
1502   else                    faceIt = SMESHUtils::elemSetIterator( theElems );
1503
1504   bool   checkUV;
1505   gp_XY  uv [9]; uv[8] = gp_XY(0,0);
1506   gp_XYZ xyz[9];
1507   vector< const SMDS_MeshNode* > nodes;
1508   SMESHDS_SubMesh*               subMeshDS = 0;
1509   TopoDS_Face                    F;
1510   Handle(Geom_Surface)           surface;
1511   TopLoc_Location                loc;
1512
1513   while ( faceIt->more() )
1514   {
1515     const SMDS_MeshElement* quad = faceIt->next();
1516     if ( !quad || quad->NbCornerNodes() != 4 )
1517       continue;
1518
1519     // get a surface the quad is on
1520
1521     if ( quad->getshapeId() < 1 )
1522     {
1523       F.Nullify();
1524       helper.SetSubShape( 0 );
1525       subMeshDS = 0;
1526     }
1527     else if ( quad->getshapeId() != helper.GetSubShapeID() )
1528     {
1529       helper.SetSubShape( quad->getshapeId() );
1530       if ( !helper.GetSubShape().IsNull() &&
1531            helper.GetSubShape().ShapeType() == TopAbs_FACE )
1532       {
1533         F = TopoDS::Face( helper.GetSubShape() );
1534         surface = BRep_Tool::Surface( F, loc );
1535         subMeshDS = GetMeshDS()->MeshElements( quad->getshapeId() );
1536       }
1537       else
1538       {
1539         helper.SetSubShape( 0 );
1540         subMeshDS = 0;
1541       }
1542     }
1543
1544     // create a central node
1545
1546     const SMDS_MeshNode* nCentral;
1547     nodes.assign( quad->begin_nodes(), quad->end_nodes() );
1548
1549     if ( nodes.size() == 9 )
1550     {
1551       nCentral = nodes.back();
1552     }
1553     else
1554     {
1555       size_t iN = 0;
1556       if ( F.IsNull() )
1557       {
1558         for ( ; iN < nodes.size(); ++iN )
1559           xyz[ iN ] = SMESH_TNodeXYZ( nodes[ iN ] );
1560
1561         for ( ; iN < 8; ++iN ) // mid-side points of a linear qudrangle
1562           xyz[ iN ] = 0.5 * ( xyz[ iN - 4 ] + xyz[( iN - 3 )%4 ] );
1563
1564         xyz[ 8 ] = helper.calcTFI( 0.5, 0.5,
1565                                    xyz[0], xyz[1], xyz[2], xyz[3],
1566                                    xyz[4], xyz[5], xyz[6], xyz[7] );
1567       }
1568       else
1569       {
1570         for ( ; iN < nodes.size(); ++iN )
1571           uv[ iN ] = helper.GetNodeUV( F, nodes[iN], nodes[(iN+2)%4], &checkUV );
1572
1573         for ( ; iN < 8; ++iN ) // UV of mid-side points of a linear qudrangle
1574           uv[ iN ] = helper.GetMiddleUV( surface, uv[ iN - 4 ], uv[( iN - 3 )%4 ] );
1575
1576         uv[ 8 ] = helper.calcTFI( 0.5, 0.5,
1577                                   uv[0], uv[1], uv[2], uv[3],
1578                                   uv[4], uv[5], uv[6], uv[7] );
1579
1580         gp_Pnt p = surface->Value( uv[8].X(), uv[8].Y() ).Transformed( loc );
1581         xyz[ 8 ] = p.XYZ();
1582       }
1583
1584       nCentral = helper.AddNode( xyz[8].X(), xyz[8].Y(), xyz[8].Z(), /*id=*/0,
1585                                  uv[8].X(), uv[8].Y() );
1586       myLastCreatedNodes.push_back( nCentral );
1587     }
1588
1589     // create 4 triangles
1590
1591     helper.SetIsQuadratic  ( nodes.size() > 4 );
1592     helper.SetIsBiQuadratic( nodes.size() == 9 );
1593     if ( helper.GetIsQuadratic() )
1594       helper.AddTLinks( static_cast< const SMDS_MeshFace*>( quad ));
1595
1596     GetMeshDS()->RemoveFreeElement( quad, subMeshDS, /*fromGroups=*/false );
1597
1598     for ( int i = 0; i < 4; ++i )
1599     {
1600       SMDS_MeshElement* tria = helper.AddFace( nodes[ i ],
1601                                                nodes[(i+1)%4],
1602                                                nCentral );
1603       ReplaceElemInGroups( tria, quad, GetMeshDS() );
1604       myLastCreatedElems.push_back( tria );
1605     }
1606   }
1607 }
1608
1609 //=======================================================================
1610 //function : BestSplit
1611 //purpose  : Find better diagonal for cutting.
1612 //=======================================================================
1613
1614 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement*              theQuad,
1615                                  SMESH::Controls::NumericalFunctorPtr theCrit)
1616 {
1617   ClearLastCreated();
1618
1619   if (!theCrit.get())
1620     return -1;
1621
1622   if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1623     return -1;
1624
1625   if( theQuad->NbNodes()==4 ||
1626       (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1627
1628     // retrieve element nodes
1629     const SMDS_MeshNode* aNodes [4];
1630     SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1631     int i = 0;
1632     //while (itN->more())
1633     while (i<4) {
1634       aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1635     }
1636     // compare two sets of possible triangles
1637     double aBadRate1, aBadRate2; // to what extent a set is bad
1638     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1639     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1640     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1641
1642     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1643     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1644     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1645     // for MaxElementLength2D functor we return minimum diagonal for splitting,
1646     // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1647     if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1648       return 1; // diagonal 1-3
1649
1650     return 2; // diagonal 2-4
1651   }
1652   return -1;
1653 }
1654
1655 namespace
1656 {
1657   // Methods of splitting volumes into tetra
1658
1659   const int theHexTo5_1[5*4+1] =
1660     {
1661       0, 1, 2, 5,    0, 4, 5, 7,     0, 2, 3, 7,    2, 5, 6, 7,     0, 5, 2, 7,   -1
1662     };
1663   const int theHexTo5_2[5*4+1] =
1664     {
1665       1, 2, 3, 6,    1, 4, 5, 6,     0, 1, 3, 4,    3, 4, 6, 7,     1, 3, 4, 6,   -1
1666     };
1667   const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1668
1669   const int theHexTo6_1[6*4+1] =
1670     {
1671       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
1672     };
1673   const int theHexTo6_2[6*4+1] =
1674     {
1675       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
1676     };
1677   const int theHexTo6_3[6*4+1] =
1678     {
1679       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
1680     };
1681   const int theHexTo6_4[6*4+1] =
1682     {
1683       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
1684     };
1685   const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1686
1687   const int thePyraTo2_1[2*4+1] =
1688     {
1689       0, 1, 2, 4,    0, 2, 3, 4,   -1
1690     };
1691   const int thePyraTo2_2[2*4+1] =
1692     {
1693       1, 2, 3, 4,    1, 3, 0, 4,   -1
1694     };
1695   const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1696
1697   const int thePentaTo3_1[3*4+1] =
1698     {
1699       0, 1, 2, 3,    1, 3, 4, 2,     2, 3, 4, 5,    -1
1700     };
1701   const int thePentaTo3_2[3*4+1] =
1702     {
1703       1, 2, 0, 4,    2, 4, 5, 0,     0, 4, 5, 3,    -1
1704     };
1705   const int thePentaTo3_3[3*4+1] =
1706     {
1707       2, 0, 1, 5,    0, 5, 3, 1,     1, 5, 3, 4,    -1
1708     };
1709   const int thePentaTo3_4[3*4+1] =
1710     {
1711       0, 1, 2, 3,    1, 3, 4, 5,     2, 3, 1, 5,    -1
1712     };
1713   const int thePentaTo3_5[3*4+1] =
1714     {
1715       1, 2, 0, 4,    2, 4, 5, 3,     0, 4, 2, 3,    -1
1716     };
1717   const int thePentaTo3_6[3*4+1] =
1718     {
1719       2, 0, 1, 5,    0, 5, 3, 4,     1, 5, 0, 4,    -1
1720     };
1721   const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1722                                 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1723
1724   // Methods of splitting hexahedron into prisms
1725
1726   const int theHexTo4Prisms_BT[6*4+1] = // bottom-top
1727     {
1728       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
1729     };
1730   const int theHexTo4Prisms_LR[6*4+1] = // left-right
1731     {
1732       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
1733     };
1734   const int theHexTo4Prisms_FB[6*4+1] = // front-back
1735     {
1736       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
1737     };
1738
1739   const int theHexTo2Prisms_BT_1[6*2+1] =
1740     {
1741       0, 1, 3, 4, 5, 7,    1, 2, 3, 5, 6, 7,   -1
1742     };
1743   const int theHexTo2Prisms_BT_2[6*2+1] =
1744     {
1745       0, 1, 2, 4, 5, 6,    0, 2, 3, 4, 6, 7,   -1
1746     };
1747   const int* theHexTo2Prisms_BT[2] = { theHexTo2Prisms_BT_1, theHexTo2Prisms_BT_2 };
1748
1749   const int theHexTo2Prisms_LR_1[6*2+1] =
1750     {
1751       1, 0, 4, 2, 3, 7,    1, 4, 5, 2, 7, 6,   -1
1752     };
1753   const int theHexTo2Prisms_LR_2[6*2+1] =
1754     {
1755       1, 0, 4, 2, 3, 7,    1, 4, 5, 2, 7, 6,   -1
1756     };
1757   const int* theHexTo2Prisms_LR[2] = { theHexTo2Prisms_LR_1, theHexTo2Prisms_LR_2 };
1758
1759   const int theHexTo2Prisms_FB_1[6*2+1] =
1760     {
1761       0, 3, 4, 1, 2, 5,    3, 7, 4, 2, 6, 5,   -1
1762     };
1763   const int theHexTo2Prisms_FB_2[6*2+1] =
1764     {
1765       0, 3, 7, 1, 2, 7,    0, 7, 4, 1, 6, 5,   -1
1766     };
1767   const int* theHexTo2Prisms_FB[2] = { theHexTo2Prisms_FB_1, theHexTo2Prisms_FB_2 };
1768
1769
1770   struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1771   {
1772     int _n1, _n2, _n3;
1773     TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1774     bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1775     bool hasAdjacentVol( const SMDS_MeshElement*    elem,
1776                          const SMDSAbs_GeometryType geom = SMDSGeom_TETRA) const;
1777   };
1778   struct TSplitMethod
1779   {
1780     int        _nbSplits;
1781     int        _nbCorners;
1782     const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1783     bool       _baryNode;     //!< additional node is to be created at cell barycenter
1784     bool       _ownConn;      //!< to delete _connectivity in destructor
1785     map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1786
1787     TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1788       : _nbSplits(nbTet), _nbCorners(4), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1789     ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1790     bool hasFacet( const TTriangleFacet& facet ) const
1791     {
1792       if ( _nbCorners == 4 )
1793       {
1794         const int* tetConn = _connectivity;
1795         for ( ; tetConn[0] >= 0; tetConn += 4 )
1796           if (( facet.contains( tetConn[0] ) +
1797                 facet.contains( tetConn[1] ) +
1798                 facet.contains( tetConn[2] ) +
1799                 facet.contains( tetConn[3] )) == 3 )
1800             return true;
1801       }
1802       else // prism, _nbCorners == 6
1803       {
1804         const int* prismConn = _connectivity;
1805         for ( ; prismConn[0] >= 0; prismConn += 6 )
1806         {
1807           if (( facet.contains( prismConn[0] ) &&
1808                 facet.contains( prismConn[1] ) &&
1809                 facet.contains( prismConn[2] ))
1810               ||
1811               ( facet.contains( prismConn[3] ) &&
1812                 facet.contains( prismConn[4] ) &&
1813                 facet.contains( prismConn[5] )))
1814             return true;
1815         }
1816       }
1817       return false;
1818     }
1819   };
1820
1821   //=======================================================================
1822   /*!
1823    * \brief return TSplitMethod for the given element to split into tetrahedra
1824    */
1825   //=======================================================================
1826
1827   TSplitMethod getTetraSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1828   {
1829     const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1830
1831     // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1832     // an edge and a face barycenter; tertaherdons are based on triangles and
1833     // a volume barycenter
1834     const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1835
1836     // Find out how adjacent volumes are split
1837
1838     vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1839     int hasAdjacentSplits = 0, maxTetConnSize = 0;
1840     for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1841     {
1842       int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1843       maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1844       if ( nbNodes < 4 ) continue;
1845
1846       list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1847       const int* nInd = vol.GetFaceNodesIndices( iF );
1848       if ( nbNodes == 4 )
1849       {
1850         TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1851         TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1852         if      ( t012.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t012 );
1853         else if ( t123.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t123 );
1854       }
1855       else
1856       {
1857         int iCom = 0; // common node of triangle faces to split into
1858         for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1859         {
1860           TTriangleFacet t012( nInd[ iQ * ( iCom             )],
1861                                nInd[ iQ * ( (iCom+1)%nbNodes )],
1862                                nInd[ iQ * ( (iCom+2)%nbNodes )]);
1863           TTriangleFacet t023( nInd[ iQ * ( iCom             )],
1864                                nInd[ iQ * ( (iCom+2)%nbNodes )],
1865                                nInd[ iQ * ( (iCom+3)%nbNodes )]);
1866           if ( t012.hasAdjacentVol( vol.Element() ) && t023.hasAdjacentVol( vol.Element() ))
1867           {
1868             triaSplits.push_back( t012 );
1869             triaSplits.push_back( t023 );
1870             break;
1871           }
1872         }
1873       }
1874       if ( !triaSplits.empty() )
1875         hasAdjacentSplits = true;
1876     }
1877
1878     // Among variants of split method select one compliant with adjacent volumes
1879
1880     TSplitMethod method;
1881     if ( !vol.Element()->IsPoly() && !is24TetMode )
1882     {
1883       int nbVariants = 2, nbTet = 0;
1884       const int** connVariants = 0;
1885       switch ( vol.Element()->GetEntityType() )
1886       {
1887       case SMDSEntity_Hexa:
1888       case SMDSEntity_Quad_Hexa:
1889       case SMDSEntity_TriQuad_Hexa:
1890         if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1891           connVariants = theHexTo5, nbTet = 5;
1892         else
1893           connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1894         break;
1895       case SMDSEntity_Pyramid:
1896       case SMDSEntity_Quad_Pyramid:
1897         connVariants = thePyraTo2;  nbTet = 2;
1898         break;
1899       case SMDSEntity_Penta:
1900       case SMDSEntity_Quad_Penta:
1901       case SMDSEntity_BiQuad_Penta:
1902         connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1903         break;
1904       default:
1905         nbVariants = 0;
1906       }
1907       for ( int variant = 0; variant < nbVariants && method._nbSplits == 0; ++variant )
1908       {
1909         // check method compliancy with adjacent tetras,
1910         // all found splits must be among facets of tetras described by this method
1911         method = TSplitMethod( nbTet, connVariants[variant] );
1912         if ( hasAdjacentSplits && method._nbSplits > 0 )
1913         {
1914           bool facetCreated = true;
1915           for ( size_t iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1916           {
1917             list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1918             for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1919               facetCreated = method.hasFacet( *facet );
1920           }
1921           if ( !facetCreated )
1922             method = TSplitMethod(0); // incompatible method
1923         }
1924       }
1925     }
1926     if ( method._nbSplits < 1 )
1927     {
1928       // No standard method is applicable, use a generic solution:
1929       // each facet of a volume is split into triangles and
1930       // each of triangles and a volume barycenter form a tetrahedron.
1931
1932       const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1933
1934       int* connectivity = new int[ maxTetConnSize + 1 ];
1935       method._connectivity = connectivity;
1936       method._ownConn = true;
1937       method._baryNode = !isHex27; // to create central node or not
1938
1939       int connSize = 0;
1940       int baryCenInd = vol.NbNodes() - int( isHex27 );
1941       for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1942       {
1943         const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1944         const int*   nInd = vol.GetFaceNodesIndices( iF );
1945         // find common node of triangle facets of tetra to create
1946         int iCommon = 0; // index in linear numeration
1947         const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1948         if ( !triaSplits.empty() )
1949         {
1950           // by found facets
1951           const TTriangleFacet* facet = &triaSplits.front();
1952           for ( ; iCommon < nbNodes-1 ; ++iCommon )
1953             if ( facet->contains( nInd[ iQ * iCommon ]) &&
1954                  facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1955               break;
1956         }
1957         else if ( nbNodes > 3 && !is24TetMode )
1958         {
1959           // find the best method of splitting into triangles by aspect ratio
1960           SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1961           map< double, int > badness2iCommon;
1962           const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1963           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1964           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1965           {
1966             double badness = 0;
1967             for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1968             {
1969               SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon         )],
1970                                       nodes[ iQ*((iLast-1)%nbNodes)],
1971                                       nodes[ iQ*((iLast  )%nbNodes)]);
1972               badness += getBadRate( &tria, aspectRatio );
1973             }
1974             badness2iCommon.insert( make_pair( badness, iCommon ));
1975           }
1976           // use iCommon with lowest badness
1977           iCommon = badness2iCommon.begin()->second;
1978         }
1979         if ( iCommon >= nbNodes )
1980           iCommon = 0; // something wrong
1981
1982         // fill connectivity of tetrahedra based on a current face
1983         int nbTet = nbNodes - 2;
1984         if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
1985         {
1986           int faceBaryCenInd;
1987           if ( isHex27 )
1988           {
1989             faceBaryCenInd = vol.GetCenterNodeIndex( iF );
1990             method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
1991           }
1992           else
1993           {
1994             method._faceBaryNode[ iF ] = 0;
1995             faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
1996           }
1997           nbTet = nbNodes;
1998           for ( int i = 0; i < nbTet; ++i )
1999           {
2000             int i1 = i, i2 = (i+1) % nbNodes;
2001             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2002             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2003             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2004             connectivity[ connSize++ ] = faceBaryCenInd;
2005             connectivity[ connSize++ ] = baryCenInd;
2006           }
2007         }
2008         else
2009         {
2010           for ( int i = 0; i < nbTet; ++i )
2011           {
2012             int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
2013             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2014             connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
2015             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2016             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2017             connectivity[ connSize++ ] = baryCenInd;
2018           }
2019         }
2020         method._nbSplits += nbTet;
2021
2022       } // loop on volume faces
2023
2024       connectivity[ connSize++ ] = -1;
2025
2026     } // end of generic solution
2027
2028     return method;
2029   }
2030   //=======================================================================
2031   /*!
2032    * \brief return TSplitMethod to split haxhedron into prisms
2033    */
2034   //=======================================================================
2035
2036   TSplitMethod getPrismSplitMethod( SMDS_VolumeTool& vol,
2037                                     const int        methodFlags,
2038                                     const int        facetToSplit)
2039   {
2040     // order of facets in HEX according to SMDS_VolumeTool::Hexa_F :
2041     // B, T, L, B, R, F
2042     const int iF = ( facetToSplit < 2 ) ? 0 : 1 + ( facetToSplit-2 ) % 2; // [0,1,2]
2043
2044     if ( methodFlags == SMESH_MeshEditor::HEXA_TO_4_PRISMS )
2045     {
2046       static TSplitMethod to4methods[4]; // order BT, LR, FB
2047       if ( to4methods[iF]._nbSplits == 0 )
2048       {
2049         switch ( iF ) {
2050         case 0:
2051           to4methods[iF]._connectivity = theHexTo4Prisms_BT;
2052           to4methods[iF]._faceBaryNode[ 0 ] = 0;
2053           to4methods[iF]._faceBaryNode[ 1 ] = 0;
2054           break;
2055         case 1:
2056           to4methods[iF]._connectivity = theHexTo4Prisms_LR;
2057           to4methods[iF]._faceBaryNode[ 2 ] = 0;
2058           to4methods[iF]._faceBaryNode[ 4 ] = 0;
2059           break;
2060         case 2:
2061           to4methods[iF]._connectivity = theHexTo4Prisms_FB;
2062           to4methods[iF]._faceBaryNode[ 3 ] = 0;
2063           to4methods[iF]._faceBaryNode[ 5 ] = 0;
2064           break;
2065         default: return to4methods[3];
2066         }
2067         to4methods[iF]._nbSplits  = 4;
2068         to4methods[iF]._nbCorners = 6;
2069       }
2070       return to4methods[iF];
2071     }
2072     // else if ( methodFlags == HEXA_TO_2_PRISMS )
2073
2074     TSplitMethod method;
2075
2076     const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2077
2078     const int nbVariants = 2, nbSplits = 2;
2079     const int** connVariants = 0;
2080     switch ( iF ) {
2081     case 0: connVariants = theHexTo2Prisms_BT; break;
2082     case 1: connVariants = theHexTo2Prisms_LR; break;
2083     case 2: connVariants = theHexTo2Prisms_FB; break;
2084     default: return method;
2085     }
2086
2087     // look for prisms adjacent via facetToSplit and an opposite one
2088     for ( int is2nd = 0; is2nd < 2; ++is2nd )
2089     {
2090       int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2091       int nbNodes = vol.NbFaceNodes( iFacet ) / iQ;
2092       if ( nbNodes != 4 ) return method;
2093
2094       const int* nInd = vol.GetFaceNodesIndices( iFacet );
2095       TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2096       TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2097       TTriangleFacet* t;
2098       if      ( t012.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2099         t = &t012;
2100       else if ( t123.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2101         t = &t123;
2102       else
2103         continue;
2104
2105       // there are adjacent prism
2106       for ( int variant = 0; variant < nbVariants; ++variant )
2107       {
2108         // check method compliancy with adjacent prisms,
2109         // the found prism facets must be among facets of prisms described by current method
2110         method._nbSplits     = nbSplits;
2111         method._nbCorners    = 6;
2112         method._connectivity = connVariants[ variant ];
2113         if ( method.hasFacet( *t ))
2114           return method;
2115       }
2116     }
2117
2118     // No adjacent prisms. Select a variant with a best aspect ratio.
2119
2120     double badness[2] = { 0., 0. };
2121     static SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
2122     const SMDS_MeshNode** nodes = vol.GetNodes();
2123     for ( int variant = 0; variant < nbVariants; ++variant )
2124       for ( int is2nd = 0; is2nd < 2; ++is2nd )
2125       {
2126         int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2127         const int*             nInd = vol.GetFaceNodesIndices( iFacet );
2128
2129         method._connectivity = connVariants[ variant ];
2130         TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2131         TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2132         TTriangleFacet* t = ( method.hasFacet( t012 )) ? & t012 : & t123;
2133
2134         SMDS_FaceOfNodes tria ( nodes[ t->_n1 ],
2135                                 nodes[ t->_n2 ],
2136                                 nodes[ t->_n3 ] );
2137         badness[ variant ] += getBadRate( &tria, aspectRatio );
2138       }
2139     const int iBetter = ( badness[1] < badness[0] && badness[0]-badness[1] > 0.1 * badness[0] );
2140
2141     method._nbSplits     = nbSplits;
2142     method._nbCorners    = 6;
2143     method._connectivity = connVariants[ iBetter ];
2144
2145     return method;
2146   }
2147
2148   //================================================================================
2149   /*!
2150    * \brief Check if there is a tetraherdon adjacent to the given element via this facet
2151    */
2152   //================================================================================
2153
2154   bool TTriangleFacet::hasAdjacentVol( const SMDS_MeshElement*    elem,
2155                                        const SMDSAbs_GeometryType geom ) const
2156   {
2157     // find the tetrahedron including the three nodes of facet
2158     const SMDS_MeshNode* n1 = elem->GetNode(_n1);
2159     const SMDS_MeshNode* n2 = elem->GetNode(_n2);
2160     const SMDS_MeshNode* n3 = elem->GetNode(_n3);
2161     SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
2162     while ( volIt1->more() )
2163     {
2164       const SMDS_MeshElement* v = volIt1->next();
2165       if ( v->GetGeomType() != geom )
2166         continue;
2167       const int lastCornerInd = v->NbCornerNodes() - 1;
2168       if ( v->IsQuadratic() && v->GetNodeIndex( n1 ) > lastCornerInd )
2169         continue; // medium node not allowed
2170       const int ind2 = v->GetNodeIndex( n2 );
2171       if ( ind2 < 0 || lastCornerInd < ind2 )
2172         continue;
2173       const int ind3 = v->GetNodeIndex( n3 );
2174       if ( ind3 < 0 || lastCornerInd < ind3 )
2175         continue;
2176       return true;
2177     }
2178     return false;
2179   }
2180
2181   //=======================================================================
2182   /*!
2183    * \brief A key of a face of volume
2184    */
2185   //=======================================================================
2186
2187   struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
2188   {
2189     TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
2190     {
2191       TIDSortedNodeSet sortedNodes;
2192       const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2193       int nbNodes = vol.NbFaceNodes( iF );
2194       const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
2195       for ( int i = 0; i < nbNodes; i += iQ )
2196         sortedNodes.insert( fNodes[i] );
2197       TIDSortedNodeSet::iterator n = sortedNodes.begin();
2198       first.first   = (*(n++))->GetID();
2199       first.second  = (*(n++))->GetID();
2200       second.first  = (*(n++))->GetID();
2201       second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
2202     }
2203   };
2204 } // namespace
2205
2206 //=======================================================================
2207 //function : SplitVolumes
2208 //purpose  : Split volume elements into tetrahedra or prisms.
2209 //           If facet ID < 0, element is split into tetrahedra,
2210 //           else a hexahedron is split into prisms so that the given facet is
2211 //           split into triangles
2212 //=======================================================================
2213
2214 void SMESH_MeshEditor::SplitVolumes (const TFacetOfElem & theElems,
2215                                      const int            theMethodFlags)
2216 {
2217   SMDS_VolumeTool    volTool;
2218   SMESH_MesherHelper helper( *GetMesh()), fHelper(*GetMesh());
2219   fHelper.ToFixNodeParameters( true );
2220
2221   SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
2222   SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
2223
2224   SMESH_SequenceOfElemPtr newNodes, newElems;
2225
2226   // map face of volume to it's baricenrtic node
2227   map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
2228   double bc[3];
2229   vector<const SMDS_MeshElement* > splitVols;
2230
2231   TFacetOfElem::const_iterator elem2facet = theElems.begin();
2232   for ( ; elem2facet != theElems.end(); ++elem2facet )
2233   {
2234     const SMDS_MeshElement* elem = elem2facet->first;
2235     const int       facetToSplit = elem2facet->second;
2236     if ( elem->GetType() != SMDSAbs_Volume )
2237       continue;
2238     const SMDSAbs_EntityType geomType = elem->GetEntityType();
2239     if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
2240       continue;
2241
2242     if ( !volTool.Set( elem, /*ignoreCentralNodes=*/false )) continue; // strange...
2243
2244     TSplitMethod splitMethod = ( facetToSplit < 0  ?
2245                                  getTetraSplitMethod( volTool, theMethodFlags ) :
2246                                  getPrismSplitMethod( volTool, theMethodFlags, facetToSplit ));
2247     if ( splitMethod._nbSplits < 1 ) continue;
2248
2249     // find submesh to add new tetras to
2250     if ( !subMesh || !subMesh->Contains( elem ))
2251     {
2252       int shapeID = FindShape( elem );
2253       helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
2254       subMesh = GetMeshDS()->MeshElements( shapeID );
2255     }
2256     int iQ;
2257     if ( elem->IsQuadratic() )
2258     {
2259       iQ = 2;
2260       // add quadratic links to the helper
2261       for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2262       {
2263         const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
2264         int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
2265         for ( int iN = 0; iN < nbN; iN += iQ )
2266           helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
2267       }
2268       helper.SetIsQuadratic( true );
2269     }
2270     else
2271     {
2272       iQ = 1;
2273       helper.SetIsQuadratic( false );
2274     }
2275     vector<const SMDS_MeshNode*> nodes( volTool.GetNodes(),
2276                                         volTool.GetNodes() + elem->NbNodes() );
2277     helper.SetElementsOnShape( true );
2278     if ( splitMethod._baryNode )
2279     {
2280       // make a node at barycenter
2281       volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
2282       SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
2283       nodes.push_back( gcNode );
2284       newNodes.push_back( gcNode );
2285     }
2286     if ( !splitMethod._faceBaryNode.empty() )
2287     {
2288       // make or find baricentric nodes of faces
2289       map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
2290       for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
2291       {
2292         map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
2293           volFace2BaryNode.insert
2294           ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
2295         if ( !f_n->second )
2296         {
2297           volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
2298           newNodes.push_back( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
2299         }
2300         nodes.push_back( iF_n->second = f_n->second );
2301       }
2302     }
2303
2304     // make new volumes
2305     splitVols.resize( splitMethod._nbSplits ); // splits of a volume
2306     const int* volConn = splitMethod._connectivity;
2307     if ( splitMethod._nbCorners == 4 ) // tetra
2308       for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2309         newElems.push_back( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2310                                                                nodes[ volConn[1] ],
2311                                                                nodes[ volConn[2] ],
2312                                                                nodes[ volConn[3] ]));
2313     else // prisms
2314       for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2315         newElems.push_back( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2316                                                                nodes[ volConn[1] ],
2317                                                                nodes[ volConn[2] ],
2318                                                                nodes[ volConn[3] ],
2319                                                                nodes[ volConn[4] ],
2320                                                                nodes[ volConn[5] ]));
2321
2322     ReplaceElemInGroups( elem, splitVols, GetMeshDS() );
2323
2324     // Split faces on sides of the split volume
2325
2326     const SMDS_MeshNode** volNodes = volTool.GetNodes();
2327     for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2328     {
2329       const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
2330       if ( nbNodes < 4 ) continue;
2331
2332       // find an existing face
2333       vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
2334                                            volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
2335       while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
2336                                                                        /*noMedium=*/false))
2337       {
2338         // make triangles
2339         helper.SetElementsOnShape( false );
2340         vector< const SMDS_MeshElement* > triangles;
2341
2342         // find submesh to add new triangles in
2343         if ( !fSubMesh || !fSubMesh->Contains( face ))
2344         {
2345           int shapeID = FindShape( face );
2346           fSubMesh = GetMeshDS()->MeshElements( shapeID );
2347         }
2348         map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
2349         if ( iF_n != splitMethod._faceBaryNode.end() )
2350         {
2351           const SMDS_MeshNode *baryNode = iF_n->second;
2352           for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
2353           {
2354             const SMDS_MeshNode* n1 = fNodes[iN];
2355             const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
2356             const SMDS_MeshNode *n3 = baryNode;
2357             if ( !volTool.IsFaceExternal( iF ))
2358               swap( n2, n3 );
2359             triangles.push_back( helper.AddFace( n1,n2,n3 ));
2360           }
2361           if ( fSubMesh ) // update position of the bary node on geometry
2362           {
2363             if ( subMesh )
2364               subMesh->RemoveNode( baryNode, false );
2365             GetMeshDS()->SetNodeOnFace( baryNode, fSubMesh->GetID() );
2366             const TopoDS_Shape& s = GetMeshDS()->IndexToShape( fSubMesh->GetID() );
2367             if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE )
2368             {
2369               fHelper.SetSubShape( s );
2370               gp_XY uv( 1e100, 1e100 );
2371               double distXYZ[4];
2372               if ( !fHelper.CheckNodeUV( TopoDS::Face( s ), baryNode,
2373                                          uv, /*tol=*/1e-7, /*force=*/true, distXYZ ) &&
2374                    uv.X() < 1e100 )
2375               {
2376                 // node is too far from the surface
2377                 GetMeshDS()->MoveNode( baryNode, distXYZ[1], distXYZ[2], distXYZ[3] );
2378                 const_cast<SMDS_MeshNode*>( baryNode )->SetPosition
2379                   ( SMDS_PositionPtr( new SMDS_FacePosition( uv.X(), uv.Y() )));
2380               }
2381             }
2382           }
2383         }
2384         else
2385         {
2386           // among possible triangles create ones described by split method
2387           const int* nInd = volTool.GetFaceNodesIndices( iF );
2388           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2389           int iCom = 0; // common node of triangle faces to split into
2390           list< TTriangleFacet > facets;
2391           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
2392           {
2393             TTriangleFacet t012( nInd[ iQ * ( iCom                )],
2394                                  nInd[ iQ * ( (iCom+1)%nbNodes )],
2395                                  nInd[ iQ * ( (iCom+2)%nbNodes )]);
2396             TTriangleFacet t023( nInd[ iQ * ( iCom                )],
2397                                  nInd[ iQ * ( (iCom+2)%nbNodes )],
2398                                  nInd[ iQ * ( (iCom+3)%nbNodes )]);
2399             if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
2400             {
2401               facets.push_back( t012 );
2402               facets.push_back( t023 );
2403               for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
2404                 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom             )],
2405                                                   nInd[ iQ * ((iLast-1)%nbNodes )],
2406                                                   nInd[ iQ * ((iLast  )%nbNodes )]));
2407               break;
2408             }
2409           }
2410           list< TTriangleFacet >::iterator facet = facets.begin();
2411           if ( facet == facets.end() )
2412             break;
2413           for ( ; facet != facets.end(); ++facet )
2414           {
2415             if ( !volTool.IsFaceExternal( iF ))
2416               swap( facet->_n2, facet->_n3 );
2417             triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
2418                                                  volNodes[ facet->_n2 ],
2419                                                  volNodes[ facet->_n3 ]));
2420           }
2421         }
2422         for ( size_t i = 0; i < triangles.size(); ++i )
2423         {
2424           if ( !triangles[ i ]) continue;
2425           if ( fSubMesh )
2426             fSubMesh->AddElement( triangles[ i ]);
2427           newElems.push_back( triangles[ i ]);
2428         }
2429         ReplaceElemInGroups( face, triangles, GetMeshDS() );
2430         GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2431
2432       } // while a face based on facet nodes exists
2433     } // loop on volume faces to split them into triangles
2434
2435     GetMeshDS()->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2436
2437     if ( geomType == SMDSEntity_TriQuad_Hexa )
2438     {
2439       // remove medium nodes that could become free
2440       for ( int i = 20; i < volTool.NbNodes(); ++i )
2441         if ( volNodes[i]->NbInverseElements() == 0 )
2442           GetMeshDS()->RemoveNode( volNodes[i] );
2443     }
2444   } // loop on volumes to split
2445
2446   myLastCreatedNodes = newNodes;
2447   myLastCreatedElems = newElems;
2448 }
2449
2450 //=======================================================================
2451 //function : GetHexaFacetsToSplit
2452 //purpose  : For hexahedra that will be split into prisms, finds facets to
2453 //           split into triangles. Only hexahedra adjacent to the one closest
2454 //           to theFacetNormal.Location() are returned.
2455 //param [in,out] theHexas - the hexahedra
2456 //param [in]     theFacetNormal - facet normal
2457 //param [out]    theFacets - the hexahedra and found facet IDs
2458 //=======================================================================
2459
2460 void SMESH_MeshEditor::GetHexaFacetsToSplit( TIDSortedElemSet& theHexas,
2461                                              const gp_Ax1&     theFacetNormal,
2462                                              TFacetOfElem &    theFacets)
2463 {
2464 #define THIS_METHOD "SMESH_MeshEditor::GetHexaFacetsToSplit(): "
2465
2466   // Find a hexa closest to the location of theFacetNormal
2467
2468   const SMDS_MeshElement* startHex;
2469   {
2470     // get SMDS_ElemIteratorPtr on theHexas
2471     typedef const SMDS_MeshElement*                                      TValue;
2472     typedef TIDSortedElemSet::iterator                                   TSetIterator;
2473     typedef SMDS::SimpleAccessor<TValue,TSetIterator>                    TAccesor;
2474     typedef SMDS_MeshElement::GeomFilter                                 TFilter;
2475     typedef SMDS_SetIterator < TValue, TSetIterator, TAccesor, TFilter > TElemSetIter;
2476     SMDS_ElemIteratorPtr elemIt = SMDS_ElemIteratorPtr
2477       ( new TElemSetIter( theHexas.begin(),
2478                           theHexas.end(),
2479                           SMDS_MeshElement::GeomFilter( SMDSGeom_HEXA )));
2480
2481     SMESH_ElementSearcher* searcher =
2482       SMESH_MeshAlgos::GetElementSearcher( *myMesh->GetMeshDS(), elemIt );
2483
2484     startHex = searcher->FindClosestTo( theFacetNormal.Location(), SMDSAbs_Volume );
2485
2486     delete searcher;
2487
2488     if ( !startHex )
2489       throw SALOME_Exception( THIS_METHOD "startHex not found");
2490   }
2491
2492   // Select a facet of startHex by theFacetNormal
2493
2494   SMDS_VolumeTool vTool( startHex );
2495   double norm[3], dot, maxDot = 0;
2496   int facetID = -1;
2497   for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2498     if ( vTool.GetFaceNormal( iF, norm[0], norm[1], norm[2] ))
2499     {
2500       dot = Abs( theFacetNormal.Direction().Dot( gp_Dir( norm[0], norm[1], norm[2] )));
2501       if ( dot > maxDot )
2502       {
2503         facetID = iF;
2504         maxDot = dot;
2505       }
2506     }
2507   if ( facetID < 0 )
2508     throw SALOME_Exception( THIS_METHOD "facet of startHex not found");
2509
2510   // Fill theFacets starting from facetID of startHex
2511
2512   // facets used for searching of volumes adjacent to already treated ones
2513   typedef pair< TFacetOfElem::iterator, int > TElemFacets;
2514   typedef map< TVolumeFaceKey, TElemFacets  > TFacetMap;
2515   TFacetMap facetsToCheck;
2516
2517   set<const SMDS_MeshNode*> facetNodes;
2518   const SMDS_MeshElement*   curHex;
2519
2520   const bool allHex = ((int) theHexas.size() == myMesh->NbHexas() );
2521
2522   while ( startHex )
2523   {
2524     // move in two directions from startHex via facetID
2525     for ( int is2nd = 0; is2nd < 2; ++is2nd )
2526     {
2527       curHex       = startHex;
2528       int curFacet = facetID;
2529       if ( is2nd ) // do not treat startHex twice
2530       {
2531         vTool.Set( curHex );
2532         if ( vTool.IsFreeFace( curFacet, &curHex ))
2533         {
2534           curHex = 0;
2535         }
2536         else
2537         {
2538           vTool.GetFaceNodes( curFacet, facetNodes );
2539           vTool.Set( curHex );
2540           curFacet = vTool.GetFaceIndex( facetNodes );
2541         }
2542       }
2543       while ( curHex )
2544       {
2545         // store a facet to split
2546         if ( curHex->GetGeomType() != SMDSGeom_HEXA )
2547         {
2548           theFacets.insert( make_pair( curHex, -1 ));
2549           break;
2550         }
2551         if ( !allHex && !theHexas.count( curHex ))
2552           break;
2553
2554         pair< TFacetOfElem::iterator, bool > facetIt2isNew =
2555           theFacets.insert( make_pair( curHex, curFacet ));
2556         if ( !facetIt2isNew.second )
2557           break;
2558
2559         // remember not-to-split facets in facetsToCheck
2560         int oppFacet = vTool.GetOppFaceIndexOfHex( curFacet );
2561         for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2562         {
2563           if ( iF == curFacet && iF == oppFacet )
2564             continue;
2565           TVolumeFaceKey facetKey ( vTool, iF );
2566           TElemFacets    elemFacet( facetIt2isNew.first, iF );
2567           pair< TFacetMap::iterator, bool > it2isnew =
2568             facetsToCheck.insert( make_pair( facetKey, elemFacet ));
2569           if ( !it2isnew.second )
2570             facetsToCheck.erase( it2isnew.first ); // adjacent hex already checked
2571         }
2572         // pass to a volume adjacent via oppFacet
2573         if ( vTool.IsFreeFace( oppFacet, &curHex ))
2574         {
2575           curHex = 0;
2576         }
2577         else
2578         {
2579           // get a new curFacet
2580           vTool.GetFaceNodes( oppFacet, facetNodes );
2581           vTool.Set( curHex );
2582           curFacet = vTool.GetFaceIndex( facetNodes, /*hint=*/curFacet );
2583         }
2584       }
2585     } // move in two directions from startHex via facetID
2586
2587     // Find a new startHex by facetsToCheck
2588
2589     startHex = 0;
2590     facetID  = -1;
2591     TFacetMap::iterator fIt = facetsToCheck.begin();
2592     while ( !startHex && fIt != facetsToCheck.end() )
2593     {
2594       const TElemFacets&  elemFacets = fIt->second;
2595       const SMDS_MeshElement*    hex = elemFacets.first->first;
2596       int                 splitFacet = elemFacets.first->second;
2597       int               lateralFacet = elemFacets.second;
2598       facetsToCheck.erase( fIt );
2599       fIt = facetsToCheck.begin();
2600
2601       vTool.Set( hex );
2602       if ( vTool.IsFreeFace( lateralFacet, &curHex ) || 
2603            curHex->GetGeomType() != SMDSGeom_HEXA )
2604         continue;
2605       if ( !allHex && !theHexas.count( curHex ))
2606         continue;
2607
2608       startHex = curHex;
2609
2610       // find a facet of startHex to split
2611
2612       set<const SMDS_MeshNode*> lateralNodes;
2613       vTool.GetFaceNodes( lateralFacet, lateralNodes );
2614       vTool.GetFaceNodes( splitFacet,   facetNodes );
2615       int oppLateralFacet = vTool.GetOppFaceIndexOfHex( lateralFacet );
2616       vTool.Set( startHex );
2617       lateralFacet = vTool.GetFaceIndex( lateralNodes, oppLateralFacet );
2618
2619       // look for a facet of startHex having common nodes with facetNodes
2620       // but not lateralFacet
2621       for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2622       {
2623         if ( iF == lateralFacet )
2624           continue;
2625         int nbCommonNodes = 0;
2626         const SMDS_MeshNode** nn = vTool.GetFaceNodes( iF );
2627         for ( int iN = 0, nbN = vTool.NbFaceNodes( iF ); iN < nbN; ++iN )
2628           nbCommonNodes += facetNodes.count( nn[ iN ]);
2629
2630         if ( nbCommonNodes >= 2 )
2631         {
2632           facetID = iF;
2633           break;
2634         }
2635       }
2636       if ( facetID < 0 )
2637         throw SALOME_Exception( THIS_METHOD "facet of a new startHex not found");
2638     }
2639   } //   while ( startHex )
2640
2641   return;
2642 }
2643
2644 namespace
2645 {
2646   //================================================================================
2647   /*!
2648    * \brief Selects nodes of several elements according to a given interlace
2649    *  \param [in] srcNodes - nodes to select from
2650    *  \param [out] tgtNodesVec - array of nodes of several elements to fill in
2651    *  \param [in] interlace - indices of nodes for all elements
2652    *  \param [in] nbElems - nb of elements
2653    *  \param [in] nbNodes - nb of nodes in each element
2654    *  \param [in] mesh - the mesh
2655    *  \param [out] elemQueue - a list to push elements found by the selected nodes
2656    *  \param [in] type - type of elements to look for
2657    */
2658   //================================================================================
2659
2660   void selectNodes( const vector< const SMDS_MeshNode* >& srcNodes,
2661                     vector< const SMDS_MeshNode* >*       tgtNodesVec,
2662                     const int*                            interlace,
2663                     const int                             nbElems,
2664                     const int                             nbNodes,
2665                     SMESHDS_Mesh*                         mesh = 0,
2666                     list< const SMDS_MeshElement* >*      elemQueue=0,
2667                     SMDSAbs_ElementType                   type=SMDSAbs_All)
2668   {
2669     for ( int iE = 0; iE < nbElems; ++iE )
2670     {
2671       vector< const SMDS_MeshNode* >& elemNodes = tgtNodesVec[iE];
2672       const int*                         select = & interlace[iE*nbNodes];
2673       elemNodes.resize( nbNodes );
2674       for ( int iN = 0; iN < nbNodes; ++iN )
2675         elemNodes[iN] = srcNodes[ select[ iN ]];
2676     }
2677     const SMDS_MeshElement* e;
2678     if ( elemQueue )
2679       for ( int iE = 0; iE < nbElems; ++iE )
2680         if (( e = mesh->FindElement( tgtNodesVec[iE], type, /*noMedium=*/false)))
2681           elemQueue->push_back( e );
2682   }
2683 }
2684
2685 //=======================================================================
2686 /*
2687  * Split bi-quadratic elements into linear ones without creation of additional nodes
2688  *   - bi-quadratic triangle will be split into 3 linear quadrangles;
2689  *   - bi-quadratic quadrangle will be split into 4 linear quadrangles;
2690  *   - tri-quadratic hexahedron will be split into 8 linear hexahedra;
2691  *   Quadratic elements of lower dimension  adjacent to the split bi-quadratic element
2692  *   will be split in order to keep the mesh conformal.
2693  *  \param elems - elements to split
2694  */
2695 //=======================================================================
2696
2697 void SMESH_MeshEditor::SplitBiQuadraticIntoLinear(TIDSortedElemSet& theElems)
2698 {
2699   vector< const SMDS_MeshNode* > elemNodes(27), subNodes[12], splitNodes[8];
2700   vector<const SMDS_MeshElement* > splitElems;
2701   list< const SMDS_MeshElement* > elemQueue;
2702   list< const SMDS_MeshElement* >::iterator elemIt;
2703
2704   SMESHDS_Mesh * mesh = GetMeshDS();
2705   ElemFeatures *elemType, hexaType(SMDSAbs_Volume), quadType(SMDSAbs_Face), segType(SMDSAbs_Edge);
2706   int nbElems, nbNodes;
2707
2708   TIDSortedElemSet::iterator elemSetIt = theElems.begin();
2709   for ( ; elemSetIt != theElems.end(); ++elemSetIt )
2710   {
2711     elemQueue.clear();
2712     elemQueue.push_back( *elemSetIt );
2713     for ( elemIt = elemQueue.begin(); elemIt != elemQueue.end(); ++elemIt )
2714     {
2715       const SMDS_MeshElement* elem = *elemIt;
2716       switch( elem->GetEntityType() )
2717       {
2718       case SMDSEntity_TriQuad_Hexa: // HEX27
2719       {
2720         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2721         nbElems  = nbNodes = 8;
2722         elemType = & hexaType;
2723
2724         // get nodes for new elements
2725         static int vInd[8][8] = {{ 0,8,20,11,   16,21,26,24 },
2726                                  { 1,9,20,8,    17,22,26,21 },
2727                                  { 2,10,20,9,   18,23,26,22 },
2728                                  { 3,11,20,10,  19,24,26,23 },
2729                                  { 16,21,26,24, 4,12,25,15  },
2730                                  { 17,22,26,21, 5,13,25,12  },
2731                                  { 18,23,26,22, 6,14,25,13  },
2732                                  { 19,24,26,23, 7,15,25,14  }};
2733         selectNodes( elemNodes, & splitNodes[0], &vInd[0][0], nbElems, nbNodes );
2734
2735         // add boundary faces to elemQueue
2736         static int fInd[6][9] = {{ 0,1,2,3, 8,9,10,11,   20 },
2737                                  { 4,5,6,7, 12,13,14,15, 25 },
2738                                  { 0,1,5,4, 8,17,12,16,  21 },
2739                                  { 1,2,6,5, 9,18,13,17,  22 },
2740                                  { 2,3,7,6, 10,19,14,18, 23 },
2741                                  { 3,0,4,7, 11,16,15,19, 24 }};
2742         selectNodes( elemNodes, & subNodes[0], &fInd[0][0], 6,9, mesh, &elemQueue, SMDSAbs_Face );
2743
2744         // add boundary segments to elemQueue
2745         static int eInd[12][3] = {{ 0,1,8 }, { 1,2,9 }, { 2,3,10 }, { 3,0,11 },
2746                                   { 4,5,12}, { 5,6,13}, { 6,7,14 }, { 7,4,15 },
2747                                   { 0,4,16}, { 1,5,17}, { 2,6,18 }, { 3,7,19 }};
2748         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 12,3, mesh, &elemQueue, SMDSAbs_Edge );
2749         break;
2750       }
2751       case SMDSEntity_BiQuad_Triangle: // TRIA7
2752       {
2753         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2754         nbElems = 3;
2755         nbNodes = 4;
2756         elemType = & quadType;
2757
2758         // get nodes for new elements
2759         static int fInd[3][4] = {{ 0,3,6,5 }, { 1,4,6,3 }, { 2,5,6,4 }};
2760         selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2761
2762         // add boundary segments to elemQueue
2763         static int eInd[3][3] = {{ 0,1,3 }, { 1,2,4 }, { 2,0,5 }};
2764         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 3,3, mesh, &elemQueue, SMDSAbs_Edge );
2765         break;
2766       }
2767       case SMDSEntity_BiQuad_Quadrangle: // QUAD9
2768       {
2769         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2770         nbElems = 4;
2771         nbNodes = 4;
2772         elemType = & quadType;
2773
2774         // get nodes for new elements
2775         static int fInd[4][4] = {{ 0,4,8,7 }, { 1,5,8,4 }, { 2,6,8,5 }, { 3,7,8,6 }};
2776         selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2777
2778         // add boundary segments to elemQueue
2779         static int eInd[4][3] = {{ 0,1,4 }, { 1,2,5 }, { 2,3,6 }, { 3,0,7 }};
2780         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 4,3, mesh, &elemQueue, SMDSAbs_Edge );
2781         break;
2782       }
2783       case SMDSEntity_Quad_Edge:
2784       {
2785         if ( elemIt == elemQueue.begin() )
2786           continue; // an elem is in theElems
2787         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2788         nbElems = 2;
2789         nbNodes = 2;
2790         elemType = & segType;
2791
2792         // get nodes for new elements
2793         static int eInd[2][2] = {{ 0,2 }, { 2,1 }};
2794         selectNodes( elemNodes, & splitNodes[0], &eInd[0][0], nbElems, nbNodes );
2795         break;
2796       }
2797       default: continue;
2798       } // switch( elem->GetEntityType() )
2799
2800       // Create new elements
2801
2802       SMESHDS_SubMesh* subMesh = mesh->MeshElements( elem->getshapeId() );
2803
2804       splitElems.clear();
2805
2806       //elemType->SetID( elem->GetID() ); // create an elem with the same ID as a removed one
2807       mesh->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2808       //splitElems.push_back( AddElement( splitNodes[ 0 ], *elemType ));
2809       //elemType->SetID( -1 );
2810
2811       for ( int iE = 0; iE < nbElems; ++iE )
2812         splitElems.push_back( AddElement( splitNodes[ iE ], *elemType ));
2813
2814
2815       ReplaceElemInGroups( elem, splitElems, mesh );
2816
2817       if ( subMesh )
2818         for ( size_t i = 0; i < splitElems.size(); ++i )
2819           subMesh->AddElement( splitElems[i] );
2820     }
2821   }
2822 }
2823
2824 //=======================================================================
2825 //function : AddToSameGroups
2826 //purpose  : add elemToAdd to the groups the elemInGroups belongs to
2827 //=======================================================================
2828
2829 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
2830                                         const SMDS_MeshElement* elemInGroups,
2831                                         SMESHDS_Mesh *          aMesh)
2832 {
2833   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2834   if (!groups.empty()) {
2835     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2836     for ( ; grIt != groups.end(); grIt++ ) {
2837       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2838       if ( group && group->Contains( elemInGroups ))
2839         group->SMDSGroup().Add( elemToAdd );
2840     }
2841   }
2842 }
2843
2844
2845 //=======================================================================
2846 //function : RemoveElemFromGroups
2847 //purpose  : Remove removeelem to the groups the elemInGroups belongs to
2848 //=======================================================================
2849 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2850                                              SMESHDS_Mesh *          aMesh)
2851 {
2852   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2853   if (!groups.empty())
2854   {
2855     set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2856     for (; GrIt != groups.end(); GrIt++)
2857     {
2858       SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2859       if (!grp || grp->IsEmpty()) continue;
2860       grp->SMDSGroup().Remove(removeelem);
2861     }
2862   }
2863 }
2864
2865 //================================================================================
2866 /*!
2867  * \brief Replace elemToRm by elemToAdd in the all groups
2868  */
2869 //================================================================================
2870
2871 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2872                                             const SMDS_MeshElement* elemToAdd,
2873                                             SMESHDS_Mesh *          aMesh)
2874 {
2875   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2876   if (!groups.empty()) {
2877     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2878     for ( ; grIt != groups.end(); grIt++ ) {
2879       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2880       if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2881         group->SMDSGroup().Add( elemToAdd );
2882     }
2883   }
2884 }
2885
2886 //================================================================================
2887 /*!
2888  * \brief Replace elemToRm by elemToAdd in the all groups
2889  */
2890 //================================================================================
2891
2892 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement*                elemToRm,
2893                                             const vector<const SMDS_MeshElement*>& elemToAdd,
2894                                             SMESHDS_Mesh *                         aMesh)
2895 {
2896   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2897   if (!groups.empty())
2898   {
2899     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2900     for ( ; grIt != groups.end(); grIt++ ) {
2901       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2902       if ( group && group->SMDSGroup().Remove( elemToRm ) )
2903         for ( size_t i = 0; i < elemToAdd.size(); ++i )
2904           group->SMDSGroup().Add( elemToAdd[ i ] );
2905     }
2906   }
2907 }
2908
2909 //=======================================================================
2910 //function : QuadToTri
2911 //purpose  : Cut quadrangles into triangles.
2912 //           theCrit is used to select a diagonal to cut
2913 //=======================================================================
2914
2915 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2916                                   const bool         the13Diag)
2917 {
2918   ClearLastCreated();
2919   myLastCreatedElems.reserve( theElems.size() * 2 );
2920
2921   SMESHDS_Mesh *       aMesh = GetMeshDS();
2922   Handle(Geom_Surface) surface;
2923   SMESH_MesherHelper   helper( *GetMesh() );
2924
2925   TIDSortedElemSet::iterator itElem;
2926   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
2927   {
2928     const SMDS_MeshElement* elem = *itElem;
2929     if ( !elem || elem->GetGeomType() != SMDSGeom_QUADRANGLE )
2930       continue;
2931
2932     if ( elem->NbNodes() == 4 ) {
2933       // retrieve element nodes
2934       const SMDS_MeshNode* aNodes [4];
2935       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2936       int i = 0;
2937       while ( itN->more() )
2938         aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2939
2940       int aShapeId = FindShape( elem );
2941       const SMDS_MeshElement* newElem1 = 0;
2942       const SMDS_MeshElement* newElem2 = 0;
2943       if ( the13Diag ) {
2944         newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2945         newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2946       }
2947       else {
2948         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2949         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2950       }
2951       myLastCreatedElems.push_back(newElem1);
2952       myLastCreatedElems.push_back(newElem2);
2953       // put a new triangle on the same shape and add to the same groups
2954       if ( aShapeId )
2955       {
2956         aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2957         aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2958       }
2959       AddToSameGroups( newElem1, elem, aMesh );
2960       AddToSameGroups( newElem2, elem, aMesh );
2961       aMesh->RemoveElement( elem );
2962     }
2963
2964     // Quadratic quadrangle
2965
2966     else if ( elem->NbNodes() >= 8 )
2967     {
2968       // get surface elem is on
2969       int aShapeId = FindShape( elem );
2970       if ( aShapeId != helper.GetSubShapeID() ) {
2971         surface.Nullify();
2972         TopoDS_Shape shape;
2973         if ( aShapeId > 0 )
2974           shape = aMesh->IndexToShape( aShapeId );
2975         if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
2976           TopoDS_Face face = TopoDS::Face( shape );
2977           surface = BRep_Tool::Surface( face );
2978           if ( !surface.IsNull() )
2979             helper.SetSubShape( shape );
2980         }
2981       }
2982
2983       const SMDS_MeshNode* aNodes [9]; aNodes[8] = 0;
2984       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2985       for ( int i = 0; itN->more(); ++i )
2986         aNodes[ i ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2987
2988       const SMDS_MeshNode* centrNode = aNodes[8];
2989       if ( centrNode == 0 )
2990       {
2991         centrNode = helper.GetCentralNode( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
2992                                            aNodes[4], aNodes[5], aNodes[6], aNodes[7],
2993                                            surface.IsNull() );
2994         myLastCreatedNodes.push_back(centrNode);
2995       }
2996
2997       // create a new element
2998       const SMDS_MeshElement* newElem1 = 0;
2999       const SMDS_MeshElement* newElem2 = 0;
3000       if ( the13Diag ) {
3001         newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
3002                                   aNodes[6], aNodes[7], centrNode );
3003         newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
3004                                   centrNode, aNodes[4], aNodes[5] );
3005       }
3006       else {
3007         newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
3008                                   aNodes[7], aNodes[4], centrNode );
3009         newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
3010                                   centrNode, aNodes[5], aNodes[6] );
3011       }
3012       myLastCreatedElems.push_back(newElem1);
3013       myLastCreatedElems.push_back(newElem2);
3014       // put a new triangle on the same shape and add to the same groups
3015       if ( aShapeId )
3016       {
3017         aMesh->SetMeshElementOnShape( newElem1, aShapeId );
3018         aMesh->SetMeshElementOnShape( newElem2, aShapeId );
3019       }
3020       AddToSameGroups( newElem1, elem, aMesh );
3021       AddToSameGroups( newElem2, elem, aMesh );
3022       aMesh->RemoveElement( elem );
3023     }
3024   }
3025
3026   return true;
3027 }
3028
3029 //=======================================================================
3030 //function : getAngle
3031 //purpose  :
3032 //=======================================================================
3033
3034 double getAngle(const SMDS_MeshElement * tr1,
3035                 const SMDS_MeshElement * tr2,
3036                 const SMDS_MeshNode *    n1,
3037                 const SMDS_MeshNode *    n2)
3038 {
3039   double angle = 2. * M_PI; // bad angle
3040
3041   // get normals
3042   SMESH::Controls::TSequenceOfXYZ P1, P2;
3043   if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
3044        !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
3045     return angle;
3046   gp_Vec N1,N2;
3047   if(!tr1->IsQuadratic())
3048     N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
3049   else
3050     N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
3051   if ( N1.SquareMagnitude() <= gp::Resolution() )
3052     return angle;
3053   if(!tr2->IsQuadratic())
3054     N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
3055   else
3056     N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
3057   if ( N2.SquareMagnitude() <= gp::Resolution() )
3058     return angle;
3059
3060   // find the first diagonal node n1 in the triangles:
3061   // take in account a diagonal link orientation
3062   const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
3063   for ( int t = 0; t < 2; t++ ) {
3064     SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
3065     int i = 0, iDiag = -1;
3066     while ( it->more()) {
3067       const SMDS_MeshElement *n = it->next();
3068       if ( n == n1 || n == n2 ) {
3069         if ( iDiag < 0)
3070           iDiag = i;
3071         else {
3072           if ( i - iDiag == 1 )
3073             nFirst[ t ] = ( n == n1 ? n2 : n1 );
3074           else
3075             nFirst[ t ] = n;
3076           break;
3077         }
3078       }
3079       i++;
3080     }
3081   }
3082   if ( nFirst[ 0 ] == nFirst[ 1 ] )
3083     N2.Reverse();
3084
3085   angle = N1.Angle( N2 );
3086   //SCRUTE( angle );
3087   return angle;
3088 }
3089
3090 // =================================================
3091 // class generating a unique ID for a pair of nodes
3092 // and able to return nodes by that ID
3093 // =================================================
3094 class LinkID_Gen {
3095 public:
3096
3097   LinkID_Gen( const SMESHDS_Mesh* theMesh )
3098     :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
3099   {}
3100
3101   long GetLinkID (const SMDS_MeshNode * n1,
3102                   const SMDS_MeshNode * n2) const
3103   {
3104     return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
3105   }
3106
3107   bool GetNodes (const long             theLinkID,
3108                  const SMDS_MeshNode* & theNode1,
3109                  const SMDS_MeshNode* & theNode2) const
3110   {
3111     theNode1 = myMesh->FindNode( theLinkID / myMaxID );
3112     if ( !theNode1 ) return false;
3113     theNode2 = myMesh->FindNode( theLinkID % myMaxID );
3114     if ( !theNode2 ) return false;
3115     return true;
3116   }
3117
3118 private:
3119   LinkID_Gen();
3120   const SMESHDS_Mesh* myMesh;
3121   long                myMaxID;
3122 };
3123
3124
3125 //=======================================================================
3126 //function : TriToQuad
3127 //purpose  : Fuse neighbour triangles into quadrangles.
3128 //           theCrit is used to select a neighbour to fuse with.
3129 //           theMaxAngle is a max angle between element normals at which
3130 //           fusion is still performed.
3131 //=======================================================================
3132
3133 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet &                   theElems,
3134                                   SMESH::Controls::NumericalFunctorPtr theCrit,
3135                                   const double                         theMaxAngle)
3136 {
3137   ClearLastCreated();
3138   myLastCreatedElems.reserve( theElems.size() / 2 );
3139
3140   if ( !theCrit.get() )
3141     return false;
3142
3143   SMESHDS_Mesh * aMesh = GetMeshDS();
3144
3145   // Prepare data for algo: build
3146   // 1. map of elements with their linkIDs
3147   // 2. map of linkIDs with their elements
3148
3149   map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
3150   map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
3151   map< const SMDS_MeshElement*, set< SMESH_TLink > >  mapEl_setLi;
3152   map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
3153
3154   TIDSortedElemSet::iterator itElem;
3155   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
3156   {
3157     const SMDS_MeshElement* elem = *itElem;
3158     if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
3159     bool IsTria = ( elem->NbCornerNodes()==3 );
3160     if (!IsTria) continue;
3161
3162     // retrieve element nodes
3163     const SMDS_MeshNode* aNodes [4];
3164     SMDS_NodeIteratorPtr itN = elem->nodeIterator();
3165     int i = 0;
3166     while ( i < 3 )
3167       aNodes[ i++ ] = itN->next();
3168     aNodes[ 3 ] = aNodes[ 0 ];
3169
3170     // fill maps
3171     for ( i = 0; i < 3; i++ ) {
3172       SMESH_TLink link( aNodes[i], aNodes[i+1] );
3173       // check if elements sharing a link can be fused
3174       itLE = mapLi_listEl.find( link );
3175       if ( itLE != mapLi_listEl.end() ) {
3176         if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
3177           continue;
3178         const SMDS_MeshElement* elem2 = (*itLE).second.front();
3179         //if ( FindShape( elem ) != FindShape( elem2 ))
3180         //  continue; // do not fuse triangles laying on different shapes
3181         if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
3182           continue; // avoid making badly shaped quads
3183         (*itLE).second.push_back( elem );
3184       }
3185       else {
3186         mapLi_listEl[ link ].push_back( elem );
3187       }
3188       mapEl_setLi [ elem ].insert( link );
3189     }
3190   }
3191   // Clean the maps from the links shared by a sole element, ie
3192   // links to which only one element is bound in mapLi_listEl
3193
3194   for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
3195     int nbElems = (*itLE).second.size();
3196     if ( nbElems < 2  ) {
3197       const SMDS_MeshElement* elem = (*itLE).second.front();
3198       SMESH_TLink link = (*itLE).first;
3199       mapEl_setLi[ elem ].erase( link );
3200       if ( mapEl_setLi[ elem ].empty() )
3201         mapEl_setLi.erase( elem );
3202     }
3203   }
3204
3205   // Algo: fuse triangles into quadrangles
3206
3207   while ( ! mapEl_setLi.empty() ) {
3208     // Look for the start element:
3209     // the element having the least nb of shared links
3210     const SMDS_MeshElement* startElem = 0;
3211     int minNbLinks = 4;
3212     for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
3213       int nbLinks = (*itEL).second.size();
3214       if ( nbLinks < minNbLinks ) {
3215         startElem = (*itEL).first;
3216         minNbLinks = nbLinks;
3217         if ( minNbLinks == 1 )
3218           break;
3219       }
3220     }
3221
3222     // search elements to fuse starting from startElem or links of elements
3223     // fused earlyer - startLinks
3224     list< SMESH_TLink > startLinks;
3225     while ( startElem || !startLinks.empty() ) {
3226       while ( !startElem && !startLinks.empty() ) {
3227         // Get an element to start, by a link
3228         SMESH_TLink linkId = startLinks.front();
3229         startLinks.pop_front();
3230         itLE = mapLi_listEl.find( linkId );
3231         if ( itLE != mapLi_listEl.end() ) {
3232           list< const SMDS_MeshElement* > & listElem = (*itLE).second;
3233           list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
3234           for ( ; itE != listElem.end() ; itE++ )
3235             if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
3236               startElem = (*itE);
3237           mapLi_listEl.erase( itLE );
3238         }
3239       }
3240
3241       if ( startElem ) {
3242         // Get candidates to be fused
3243         const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
3244         const SMESH_TLink *link12 = 0, *link13 = 0;
3245         startElem = 0;
3246         ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
3247         set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
3248         ASSERT( !setLi.empty() );
3249         set< SMESH_TLink >::iterator itLi;
3250         for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
3251         {
3252           const SMESH_TLink & link = (*itLi);
3253           itLE = mapLi_listEl.find( link );
3254           if ( itLE == mapLi_listEl.end() )
3255             continue;
3256
3257           const SMDS_MeshElement* elem = (*itLE).second.front();
3258           if ( elem == tr1 )
3259             elem = (*itLE).second.back();
3260           mapLi_listEl.erase( itLE );
3261           if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
3262             continue;
3263           if ( tr2 ) {
3264             tr3 = elem;
3265             link13 = &link;
3266           }
3267           else {
3268             tr2 = elem;
3269             link12 = &link;
3270           }
3271
3272           // add other links of elem to list of links to re-start from
3273           set< SMESH_TLink >& links = mapEl_setLi[ elem ];
3274           set< SMESH_TLink >::iterator it;
3275           for ( it = links.begin(); it != links.end(); it++ ) {
3276             const SMESH_TLink& link2 = (*it);
3277             if ( link2 != link )
3278               startLinks.push_back( link2 );
3279           }
3280         }
3281
3282         // Get nodes of possible quadrangles
3283         const SMDS_MeshNode *n12 [4], *n13 [4];
3284         bool Ok12 = false, Ok13 = false;
3285         const SMDS_MeshNode *linkNode1, *linkNode2;
3286         if(tr2) {
3287           linkNode1 = link12->first;
3288           linkNode2 = link12->second;
3289           if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
3290             Ok12 = true;
3291         }
3292         if(tr3) {
3293           linkNode1 = link13->first;
3294           linkNode2 = link13->second;
3295           if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
3296             Ok13 = true;
3297         }
3298
3299         // Choose a pair to fuse
3300         if ( Ok12 && Ok13 ) {
3301           SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
3302           SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
3303           double aBadRate12 = getBadRate( &quad12, theCrit );
3304           double aBadRate13 = getBadRate( &quad13, theCrit );
3305           if (  aBadRate13 < aBadRate12 )
3306             Ok12 = false;
3307           else
3308             Ok13 = false;
3309         }
3310
3311         // Make quadrangles
3312         // and remove fused elems and remove links from the maps
3313         mapEl_setLi.erase( tr1 );
3314         if ( Ok12 )
3315         {
3316           mapEl_setLi.erase( tr2 );
3317           mapLi_listEl.erase( *link12 );
3318           if ( tr1->NbNodes() == 3 )
3319           {
3320             const SMDS_MeshElement* newElem = 0;
3321             newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
3322             myLastCreatedElems.push_back(newElem);
3323             AddToSameGroups( newElem, tr1, aMesh );
3324             int aShapeId = tr1->getshapeId();
3325             if ( aShapeId )
3326               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3327             aMesh->RemoveElement( tr1 );
3328             aMesh->RemoveElement( tr2 );
3329           }
3330           else {
3331             vector< const SMDS_MeshNode* > N1;
3332             vector< const SMDS_MeshNode* > N2;
3333             getNodesFromTwoTria(tr1,tr2,N1,N2);
3334             // now we receive following N1 and N2 (using numeration as in image in InverseDiag())
3335             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
3336             // i.e. first nodes from both arrays form a new diagonal
3337             const SMDS_MeshNode* aNodes[8];
3338             aNodes[0] = N1[0];
3339             aNodes[1] = N1[1];
3340             aNodes[2] = N2[0];
3341             aNodes[3] = N2[1];
3342             aNodes[4] = N1[3];
3343             aNodes[5] = N2[5];
3344             aNodes[6] = N2[3];
3345             aNodes[7] = N1[5];
3346             const SMDS_MeshElement* newElem = 0;
3347             if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3348               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3349                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3350             else
3351               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3352                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3353             myLastCreatedElems.push_back(newElem);
3354             AddToSameGroups( newElem, tr1, aMesh );
3355             int aShapeId = tr1->getshapeId();
3356             if ( aShapeId )
3357               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3358             aMesh->RemoveElement( tr1 );
3359             aMesh->RemoveElement( tr2 );
3360             // remove middle node (9)
3361             if ( N1[4]->NbInverseElements() == 0 )
3362               aMesh->RemoveNode( N1[4] );
3363             if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3364               aMesh->RemoveNode( N1[6] );
3365             if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3366               aMesh->RemoveNode( N2[6] );
3367           }
3368         }
3369         else if ( Ok13 )
3370         {
3371           mapEl_setLi.erase( tr3 );
3372           mapLi_listEl.erase( *link13 );
3373           if ( tr1->NbNodes() == 3 ) {
3374             const SMDS_MeshElement* newElem = 0;
3375             newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
3376             myLastCreatedElems.push_back(newElem);
3377             AddToSameGroups( newElem, tr1, aMesh );
3378             int aShapeId = tr1->getshapeId();
3379             if ( aShapeId )
3380               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3381             aMesh->RemoveElement( tr1 );
3382             aMesh->RemoveElement( tr3 );
3383           }
3384           else {
3385             vector< const SMDS_MeshNode* > N1;
3386             vector< const SMDS_MeshNode* > N2;
3387             getNodesFromTwoTria(tr1,tr3,N1,N2);
3388             // now we receive following N1 and N2 (using numeration as above image)
3389             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
3390             // i.e. first nodes from both arrays form a new diagonal
3391             const SMDS_MeshNode* aNodes[8];
3392             aNodes[0] = N1[0];
3393             aNodes[1] = N1[1];
3394             aNodes[2] = N2[0];
3395             aNodes[3] = N2[1];
3396             aNodes[4] = N1[3];
3397             aNodes[5] = N2[5];
3398             aNodes[6] = N2[3];
3399             aNodes[7] = N1[5];
3400             const SMDS_MeshElement* newElem = 0;
3401             if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3402               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3403                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3404             else
3405               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3406                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3407             myLastCreatedElems.push_back(newElem);
3408             AddToSameGroups( newElem, tr1, aMesh );
3409             int aShapeId = tr1->getshapeId();
3410             if ( aShapeId )
3411               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3412             aMesh->RemoveElement( tr1 );
3413             aMesh->RemoveElement( tr3 );
3414             // remove middle node (9)
3415             if ( N1[4]->NbInverseElements() == 0 )
3416               aMesh->RemoveNode( N1[4] );
3417             if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3418               aMesh->RemoveNode( N1[6] );
3419             if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3420               aMesh->RemoveNode( N2[6] );
3421           }
3422         }
3423
3424         // Next element to fuse: the rejected one
3425         if ( tr3 )
3426           startElem = Ok12 ? tr3 : tr2;
3427
3428       } // if ( startElem )
3429     } // while ( startElem || !startLinks.empty() )
3430   } // while ( ! mapEl_setLi.empty() )
3431
3432   return true;
3433 }
3434
3435 //================================================================================
3436 /*!
3437  * \brief Return nodes linked to the given one
3438  * \param theNode - the node
3439  * \param linkedNodes - the found nodes
3440  * \param type - the type of elements to check
3441  *
3442  * Medium nodes are ignored
3443  */
3444 //================================================================================
3445
3446 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
3447                                        TIDSortedElemSet &   linkedNodes,
3448                                        SMDSAbs_ElementType  type )
3449 {
3450   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
3451   while ( elemIt->more() )
3452   {
3453     const SMDS_MeshElement* elem = elemIt->next();
3454     if(elem->GetType() == SMDSAbs_0DElement)
3455       continue;
3456
3457     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
3458     if ( elem->GetType() == SMDSAbs_Volume )
3459     {
3460       SMDS_VolumeTool vol( elem );
3461       while ( nodeIt->more() ) {
3462         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3463         if ( theNode != n && vol.IsLinked( theNode, n ))
3464           linkedNodes.insert( n );
3465       }
3466     }
3467     else
3468     {
3469       for ( int i = 0; nodeIt->more(); ++i ) {
3470         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3471         if ( n == theNode ) {
3472           int iBefore = i - 1;
3473           int iAfter  = i + 1;
3474           if ( elem->IsQuadratic() ) {
3475             int nb = elem->NbNodes() / 2;
3476             iAfter  = SMESH_MesherHelper::WrapIndex( iAfter, nb );
3477             iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
3478           }
3479           linkedNodes.insert( elem->GetNodeWrap( iAfter ));
3480           linkedNodes.insert( elem->GetNodeWrap( iBefore ));
3481         }
3482       }
3483     }
3484   }
3485 }
3486
3487 //=======================================================================
3488 //function : laplacianSmooth
3489 //purpose  : pulls theNode toward the center of surrounding nodes directly
3490 //           connected to that node along an element edge
3491 //=======================================================================
3492
3493 void laplacianSmooth(const SMDS_MeshNode*                 theNode,
3494                      const Handle(Geom_Surface)&          theSurface,
3495                      map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3496 {
3497   // find surrounding nodes
3498
3499   TIDSortedElemSet nodeSet;
3500   SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
3501
3502   // compute new coodrs
3503
3504   double coord[] = { 0., 0., 0. };
3505   TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3506   for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
3507     const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3508     if ( theSurface.IsNull() ) { // smooth in 3D
3509       coord[0] += node->X();
3510       coord[1] += node->Y();
3511       coord[2] += node->Z();
3512     }
3513     else { // smooth in 2D
3514       ASSERT( theUVMap.find( node ) != theUVMap.end() );
3515       gp_XY* uv = theUVMap[ node ];
3516       coord[0] += uv->X();
3517       coord[1] += uv->Y();
3518     }
3519   }
3520   int nbNodes = nodeSet.size();
3521   if ( !nbNodes )
3522     return;
3523   coord[0] /= nbNodes;
3524   coord[1] /= nbNodes;
3525
3526   if ( !theSurface.IsNull() ) {
3527     ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3528     theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3529     gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3530     coord[0] = p3d.X();
3531     coord[1] = p3d.Y();
3532     coord[2] = p3d.Z();
3533   }
3534   else
3535     coord[2] /= nbNodes;
3536
3537   // move node
3538
3539   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3540 }
3541
3542 //=======================================================================
3543 //function : centroidalSmooth
3544 //purpose  : pulls theNode toward the element-area-weighted centroid of the
3545 //           surrounding elements
3546 //=======================================================================
3547
3548 void centroidalSmooth(const SMDS_MeshNode*                 theNode,
3549                       const Handle(Geom_Surface)&          theSurface,
3550                       map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3551 {
3552   gp_XYZ aNewXYZ(0.,0.,0.);
3553   SMESH::Controls::Area anAreaFunc;
3554   double totalArea = 0.;
3555   int nbElems = 0;
3556
3557   // compute new XYZ
3558
3559   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3560   while ( elemIt->more() )
3561   {
3562     const SMDS_MeshElement* elem = elemIt->next();
3563     nbElems++;
3564
3565     gp_XYZ elemCenter(0.,0.,0.);
3566     SMESH::Controls::TSequenceOfXYZ aNodePoints;
3567     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3568     int nn = elem->NbNodes();
3569     if(elem->IsQuadratic()) nn = nn/2;
3570     int i=0;
3571     //while ( itN->more() ) {
3572     while ( i<nn ) {
3573       const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3574       i++;
3575       gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3576       aNodePoints.push_back( aP );
3577       if ( !theSurface.IsNull() ) { // smooth in 2D
3578         ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3579         gp_XY* uv = theUVMap[ aNode ];
3580         aP.SetCoord( uv->X(), uv->Y(), 0. );
3581       }
3582       elemCenter += aP;
3583     }
3584     double elemArea = anAreaFunc.GetValue( aNodePoints );
3585     totalArea += elemArea;
3586     elemCenter /= nn;
3587     aNewXYZ += elemCenter * elemArea;
3588   }
3589   aNewXYZ /= totalArea;
3590   if ( !theSurface.IsNull() ) {
3591     theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3592     aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3593   }
3594
3595   // move node
3596
3597   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3598 }
3599
3600 //=======================================================================
3601 //function : getClosestUV
3602 //purpose  : return UV of closest projection
3603 //=======================================================================
3604
3605 static bool getClosestUV (Extrema_GenExtPS& projector,
3606                           const gp_Pnt&     point,
3607                           gp_XY &           result)
3608 {
3609   projector.Perform( point );
3610   if ( projector.IsDone() ) {
3611     double u, v, minVal = DBL_MAX;
3612     for ( int i = projector.NbExt(); i > 0; i-- )
3613       if ( projector.SquareDistance( i ) < minVal ) {
3614         minVal = projector.SquareDistance( i );
3615         projector.Point( i ).Parameter( u, v );
3616       }
3617     result.SetCoord( u, v );
3618     return true;
3619   }
3620   return false;
3621 }
3622
3623 //=======================================================================
3624 //function : Smooth
3625 //purpose  : Smooth theElements during theNbIterations or until a worst
3626 //           element has aspect ratio <= theTgtAspectRatio.
3627 //           Aspect Ratio varies in range [1.0, inf].
3628 //           If theElements is empty, the whole mesh is smoothed.
3629 //           theFixedNodes contains additionally fixed nodes. Nodes built
3630 //           on edges and boundary nodes are always fixed.
3631 //=======================================================================
3632
3633 void SMESH_MeshEditor::Smooth (TIDSortedElemSet &          theElems,
3634                                set<const SMDS_MeshNode*> & theFixedNodes,
3635                                const SmoothMethod          theSmoothMethod,
3636                                const int                   theNbIterations,
3637                                double                      theTgtAspectRatio,
3638                                const bool                  the2D)
3639 {
3640   ClearLastCreated();
3641
3642   if ( theTgtAspectRatio < 1.0 )
3643     theTgtAspectRatio = 1.0;
3644
3645   const double disttol = 1.e-16;
3646
3647   SMESH::Controls::AspectRatio aQualityFunc;
3648
3649   SMESHDS_Mesh* aMesh = GetMeshDS();
3650
3651   if ( theElems.empty() ) {
3652     // add all faces to theElems
3653     SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3654     while ( fIt->more() ) {
3655       const SMDS_MeshElement* face = fIt->next();
3656       theElems.insert( theElems.end(), face );
3657     }
3658   }
3659   // get all face ids theElems are on
3660   set< int > faceIdSet;
3661   TIDSortedElemSet::iterator itElem;
3662   if ( the2D )
3663     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3664       int fId = FindShape( *itElem );
3665       // check that corresponding submesh exists and a shape is face
3666       if (fId &&
3667           faceIdSet.find( fId ) == faceIdSet.end() &&
3668           aMesh->MeshElements( fId )) {
3669         TopoDS_Shape F = aMesh->IndexToShape( fId );
3670         if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3671           faceIdSet.insert( fId );
3672       }
3673     }
3674   faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3675
3676   // ===============================================
3677   // smooth elements on each TopoDS_Face separately
3678   // ===============================================
3679
3680   SMESH_MesherHelper helper( *GetMesh() );
3681
3682   set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treat 0 fId at the end
3683   for ( ; fId != faceIdSet.rend(); ++fId )
3684   {
3685     // get face surface and submesh
3686     Handle(Geom_Surface) surface;
3687     SMESHDS_SubMesh* faceSubMesh = 0;
3688     TopoDS_Face face;
3689     double fToler2 = 0;
3690     double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3691     bool isUPeriodic = false, isVPeriodic = false;
3692     if ( *fId )
3693     {
3694       face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3695       surface = BRep_Tool::Surface( face );
3696       faceSubMesh = aMesh->MeshElements( *fId );
3697       fToler2 = BRep_Tool::Tolerance( face );
3698       fToler2 *= fToler2 * 10.;
3699       isUPeriodic = surface->IsUPeriodic();
3700       // if ( isUPeriodic )
3701       //   surface->UPeriod();
3702       isVPeriodic = surface->IsVPeriodic();
3703       // if ( isVPeriodic )
3704       //   surface->VPeriod();
3705       surface->Bounds( u1, u2, v1, v2 );
3706       helper.SetSubShape( face );
3707     }
3708     // ---------------------------------------------------------
3709     // for elements on a face, find movable and fixed nodes and
3710     // compute UV for them
3711     // ---------------------------------------------------------
3712     bool checkBoundaryNodes = false;
3713     bool isQuadratic = false;
3714     set<const SMDS_MeshNode*> setMovableNodes;
3715     map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
3716     list< gp_XY > listUV; // uvs the 2 uvMaps refer to
3717     list< const SMDS_MeshElement* > elemsOnFace;
3718
3719     Extrema_GenExtPS projector;
3720     GeomAdaptor_Surface surfAdaptor;
3721     if ( !surface.IsNull() ) {
3722       surfAdaptor.Load( surface );
3723       projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
3724     }
3725     int nbElemOnFace = 0;
3726     itElem = theElems.begin();
3727     // loop on not yet smoothed elements: look for elems on a face
3728     while ( itElem != theElems.end() )
3729     {
3730       if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
3731         break; // all elements found
3732
3733       const SMDS_MeshElement* elem = *itElem;
3734       if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
3735            ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
3736         ++itElem;
3737         continue;
3738       }
3739       elemsOnFace.push_back( elem );
3740       theElems.erase( itElem++ );
3741       nbElemOnFace++;
3742
3743       if ( !isQuadratic )
3744         isQuadratic = elem->IsQuadratic();
3745
3746       // get movable nodes of elem
3747       const SMDS_MeshNode* node;
3748       SMDS_TypeOfPosition posType;
3749       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3750       int nn = 0, nbn =  elem->NbNodes();
3751       if(elem->IsQuadratic())
3752         nbn = nbn/2;
3753       while ( nn++ < nbn ) {
3754         node = static_cast<const SMDS_MeshNode*>( itN->next() );
3755         const SMDS_PositionPtr& pos = node->GetPosition();
3756         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3757         if (posType != SMDS_TOP_EDGE &&
3758             posType != SMDS_TOP_VERTEX &&
3759             theFixedNodes.find( node ) == theFixedNodes.end())
3760         {
3761           // check if all faces around the node are on faceSubMesh
3762           // because a node on edge may be bound to face
3763           bool all = true;
3764           if ( faceSubMesh ) {
3765             SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3766             while ( eIt->more() && all ) {
3767               const SMDS_MeshElement* e = eIt->next();
3768               all = faceSubMesh->Contains( e );
3769             }
3770           }
3771           if ( all )
3772             setMovableNodes.insert( node );
3773           else
3774             checkBoundaryNodes = true;
3775         }
3776         if ( posType == SMDS_TOP_3DSPACE )
3777           checkBoundaryNodes = true;
3778       }
3779
3780       if ( surface.IsNull() )
3781         continue;
3782
3783       // get nodes to check UV
3784       list< const SMDS_MeshNode* > uvCheckNodes;
3785       const SMDS_MeshNode* nodeInFace = 0;
3786       itN = elem->nodesIterator();
3787       nn = 0; nbn =  elem->NbNodes();
3788       if(elem->IsQuadratic())
3789         nbn = nbn/2;
3790       while ( nn++ < nbn ) {
3791         node = static_cast<const SMDS_MeshNode*>( itN->next() );
3792         if ( node->GetPosition()->GetDim() == 2 )
3793           nodeInFace = node;
3794         if ( uvMap.find( node ) == uvMap.end() )
3795           uvCheckNodes.push_back( node );
3796         // add nodes of elems sharing node
3797         //         SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3798         //         while ( eIt->more() ) {
3799         //           const SMDS_MeshElement* e = eIt->next();
3800         //           if ( e != elem ) {
3801         //             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3802         //             while ( nIt->more() ) {
3803         //               const SMDS_MeshNode* n =
3804         //                 static_cast<const SMDS_MeshNode*>( nIt->next() );
3805         //               if ( uvMap.find( n ) == uvMap.end() )
3806         //                 uvCheckNodes.push_back( n );
3807         //             }
3808         //           }
3809         //         }
3810       }
3811       // check UV on face
3812       list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
3813       for ( ; n != uvCheckNodes.end(); ++n ) {
3814         node = *n;
3815         gp_XY uv( 0, 0 );
3816         const SMDS_PositionPtr& pos = node->GetPosition();
3817         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3818         // get existing UV
3819         if ( pos )
3820         {
3821           bool toCheck = true;
3822           uv = helper.GetNodeUV( face, node, nodeInFace, &toCheck );
3823         }
3824         // compute not existing UV
3825         bool project = ( posType == SMDS_TOP_3DSPACE );
3826         // double dist1 = DBL_MAX, dist2 = 0;
3827         // if ( posType != SMDS_TOP_3DSPACE ) {
3828         //   dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
3829         //   project = dist1 > fToler2;
3830         // }
3831         if ( project ) { // compute new UV
3832           gp_XY newUV;
3833           gp_Pnt pNode = SMESH_TNodeXYZ( node );
3834           if ( !getClosestUV( projector, pNode, newUV )) {
3835             MESSAGE("Node Projection Failed " << node);
3836           }
3837           else {
3838             if ( isUPeriodic )
3839               newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
3840             if ( isVPeriodic )
3841               newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
3842             // check new UV
3843             // if ( posType != SMDS_TOP_3DSPACE )
3844             //   dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
3845             // if ( dist2 < dist1 )
3846             uv = newUV;
3847           }
3848         }
3849         // store UV in the map
3850         listUV.push_back( uv );
3851         uvMap.insert( make_pair( node, &listUV.back() ));
3852       }
3853     } // loop on not yet smoothed elements
3854
3855     if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
3856       checkBoundaryNodes = true;
3857
3858     // fix nodes on mesh boundary
3859
3860     if ( checkBoundaryNodes ) {
3861       map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
3862       map< SMESH_TLink, int >::iterator link_nb;
3863       // put all elements links to linkNbMap
3864       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3865       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3866         const SMDS_MeshElement* elem = (*elemIt);
3867         int nbn =  elem->NbCornerNodes();
3868         // loop on elem links: insert them in linkNbMap
3869         for ( int iN = 0; iN < nbn; ++iN ) {
3870           const SMDS_MeshNode* n1 = elem->GetNode( iN );
3871           const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
3872           SMESH_TLink link( n1, n2 );
3873           link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
3874           link_nb->second++;
3875         }
3876       }
3877       // remove nodes that are in links encountered only once from setMovableNodes
3878       for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
3879         if ( link_nb->second == 1 ) {
3880           setMovableNodes.erase( link_nb->first.node1() );
3881           setMovableNodes.erase( link_nb->first.node2() );
3882         }
3883       }
3884     }
3885
3886     // -----------------------------------------------------
3887     // for nodes on seam edge, compute one more UV ( uvMap2 );
3888     // find movable nodes linked to nodes on seam and which
3889     // are to be smoothed using the second UV ( uvMap2 )
3890     // -----------------------------------------------------
3891
3892     set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
3893     if ( !surface.IsNull() ) {
3894       TopExp_Explorer eExp( face, TopAbs_EDGE );
3895       for ( ; eExp.More(); eExp.Next() ) {
3896         TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
3897         if ( !BRep_Tool::IsClosed( edge, face ))
3898           continue;
3899         SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
3900         if ( !sm ) continue;
3901         // find out which parameter varies for a node on seam
3902         double f,l;
3903         gp_Pnt2d uv1, uv2;
3904         Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3905         if ( pcurve.IsNull() ) continue;
3906         uv1 = pcurve->Value( f );
3907         edge.Reverse();
3908         pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3909         if ( pcurve.IsNull() ) continue;
3910         uv2 = pcurve->Value( f );
3911         int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
3912         // assure uv1 < uv2
3913         if ( uv1.Coord( iPar ) > uv2.Coord( iPar ))
3914           std::swap( uv1, uv2 );
3915         // get nodes on seam and its vertices
3916         list< const SMDS_MeshNode* > seamNodes;
3917         SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
3918         while ( nSeamIt->more() ) {
3919           const SMDS_MeshNode* node = nSeamIt->next();
3920           if ( !isQuadratic || !IsMedium( node ))
3921             seamNodes.push_back( node );
3922         }
3923         TopExp_Explorer vExp( edge, TopAbs_VERTEX );
3924         for ( ; vExp.More(); vExp.Next() ) {
3925           sm = aMesh->MeshElements( vExp.Current() );
3926           if ( sm ) {
3927             nSeamIt = sm->GetNodes();
3928             while ( nSeamIt->more() )
3929               seamNodes.push_back( nSeamIt->next() );
3930           }
3931         }
3932         // loop on nodes on seam
3933         list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
3934         for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
3935           const SMDS_MeshNode* nSeam = *noSeIt;
3936           map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
3937           if ( n_uv == uvMap.end() )
3938             continue;
3939           // set the first UV
3940           n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
3941           // set the second UV
3942           listUV.push_back( *n_uv->second );
3943           listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
3944           if ( uvMap2.empty() )
3945             uvMap2 = uvMap; // copy the uvMap contents
3946           uvMap2[ nSeam ] = &listUV.back();
3947
3948           // collect movable nodes linked to ones on seam in nodesNearSeam
3949           SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
3950           while ( eIt->more() ) {
3951             const SMDS_MeshElement* e = eIt->next();
3952             int nbUseMap1 = 0, nbUseMap2 = 0;
3953             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3954             int nn = 0, nbn =  e->NbNodes();
3955             if(e->IsQuadratic()) nbn = nbn/2;
3956             while ( nn++ < nbn )
3957             {
3958               const SMDS_MeshNode* n =
3959                 static_cast<const SMDS_MeshNode*>( nIt->next() );
3960               if (n == nSeam ||
3961                   setMovableNodes.find( n ) == setMovableNodes.end() )
3962                 continue;
3963               // add only nodes being closer to uv2 than to uv1
3964               // gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
3965               //              0.5 * ( n->Y() + nSeam->Y() ),
3966               //              0.5 * ( n->Z() + nSeam->Z() ));
3967               // gp_XY uv;
3968               // getClosestUV( projector, pMid, uv );
3969               double x = uvMap[ n ]->Coord( iPar );
3970               if ( Abs( uv1.Coord( iPar ) - x ) >
3971                    Abs( uv2.Coord( iPar ) - x )) {
3972                 nodesNearSeam.insert( n );
3973                 nbUseMap2++;
3974               }
3975               else
3976                 nbUseMap1++;
3977             }
3978             // for centroidalSmooth all element nodes must
3979             // be on one side of a seam
3980             if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
3981               SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3982               nn = 0;
3983               while ( nn++ < nbn ) {
3984                 const SMDS_MeshNode* n =
3985                   static_cast<const SMDS_MeshNode*>( nIt->next() );
3986                 setMovableNodes.erase( n );
3987               }
3988             }
3989           }
3990         } // loop on nodes on seam
3991       } // loop on edge of a face
3992     } // if ( !face.IsNull() )
3993
3994     if ( setMovableNodes.empty() ) {
3995       MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
3996       continue; // goto next face
3997     }
3998
3999     // -------------
4000     // SMOOTHING //
4001     // -------------
4002
4003     int it = -1;
4004     double maxRatio = -1., maxDisplacement = -1.;
4005     set<const SMDS_MeshNode*>::iterator nodeToMove;
4006     for ( it = 0; it < theNbIterations; it++ ) {
4007       maxDisplacement = 0.;
4008       nodeToMove = setMovableNodes.begin();
4009       for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4010         const SMDS_MeshNode* node = (*nodeToMove);
4011         gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
4012
4013         // smooth
4014         bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
4015         if ( theSmoothMethod == LAPLACIAN )
4016           laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
4017         else
4018           centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
4019
4020         // node displacement
4021         gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
4022         Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
4023         if ( aDispl > maxDisplacement )
4024           maxDisplacement = aDispl;
4025       }
4026       // no node movement => exit
4027       //if ( maxDisplacement < 1.e-16 ) {
4028       if ( maxDisplacement < disttol ) {
4029         MESSAGE("-- no node movement --");
4030         break;
4031       }
4032
4033       // check elements quality
4034       maxRatio  = 0;
4035       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4036       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4037         const SMDS_MeshElement* elem = (*elemIt);
4038         if ( !elem || elem->GetType() != SMDSAbs_Face )
4039           continue;
4040         SMESH::Controls::TSequenceOfXYZ aPoints;
4041         if ( aQualityFunc.GetPoints( elem, aPoints )) {
4042           double aValue = aQualityFunc.GetValue( aPoints );
4043           if ( aValue > maxRatio )
4044             maxRatio = aValue;
4045         }
4046       }
4047       if ( maxRatio <= theTgtAspectRatio ) {
4048         //MESSAGE("-- quality achieved --");
4049         break;
4050       }
4051       if (it+1 == theNbIterations) {
4052         //MESSAGE("-- Iteration limit exceeded --");
4053       }
4054     } // smoothing iterations
4055
4056     // MESSAGE(" Face id: " << *fId <<
4057     //         " Nb iterstions: " << it <<
4058     //         " Displacement: " << maxDisplacement <<
4059     //         " Aspect Ratio " << maxRatio);
4060
4061     // ---------------------------------------
4062     // new nodes positions are computed,
4063     // record movement in DS and set new UV
4064     // ---------------------------------------
4065     nodeToMove = setMovableNodes.begin();
4066     for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4067       SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
4068       aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
4069       map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
4070       if ( node_uv != uvMap.end() ) {
4071         gp_XY* uv = node_uv->second;
4072         node->SetPosition
4073           ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
4074       }
4075     }
4076
4077     // move medium nodes of quadratic elements
4078     if ( isQuadratic )
4079     {
4080       vector<const SMDS_MeshNode*> nodes;
4081       bool checkUV;
4082       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4083       for ( ; elemIt != elemsOnFace.end(); ++elemIt )
4084       {
4085         const SMDS_MeshElement* QF = *elemIt;
4086         if ( QF->IsQuadratic() )
4087         {
4088           nodes.assign( SMDS_MeshElement::iterator( QF->interlacedNodesElemIterator() ),
4089                         SMDS_MeshElement::iterator() );
4090           nodes.push_back( nodes[0] );
4091           gp_Pnt xyz;
4092           for (size_t i = 1; i < nodes.size(); i += 2 ) // i points to a medium node
4093           {
4094             if ( !surface.IsNull() )
4095             {
4096               gp_XY uv1 = helper.GetNodeUV( face, nodes[i-1], nodes[i+1], &checkUV );
4097               gp_XY uv2 = helper.GetNodeUV( face, nodes[i+1], nodes[i-1], &checkUV );
4098               gp_XY uv  = helper.GetMiddleUV( surface, uv1, uv2 );
4099               xyz = surface->Value( uv.X(), uv.Y() );
4100             }
4101             else {
4102               xyz = 0.5 * ( SMESH_TNodeXYZ( nodes[i-1] ) + SMESH_TNodeXYZ( nodes[i+1] ));
4103             }
4104             if (( SMESH_TNodeXYZ( nodes[i] ) - xyz.XYZ() ).Modulus() > disttol )
4105               // we have to move a medium node
4106               aMesh->MoveNode( nodes[i], xyz.X(), xyz.Y(), xyz.Z() );
4107           }
4108         }
4109       }
4110     }
4111
4112   } // loop on face ids
4113
4114 }
4115
4116 namespace
4117 {
4118   //=======================================================================
4119   //function : isReverse
4120   //purpose  : Return true if normal of prevNodes is not co-directied with
4121   //           gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
4122   //           iNotSame is where prevNodes and nextNodes are different.
4123   //           If result is true then future volume orientation is OK
4124   //=======================================================================
4125
4126   bool isReverse(const SMDS_MeshElement*             face,
4127                  const vector<const SMDS_MeshNode*>& prevNodes,
4128                  const vector<const SMDS_MeshNode*>& nextNodes,
4129                  const int                           iNotSame)
4130   {
4131
4132     SMESH_TNodeXYZ pP = prevNodes[ iNotSame ];
4133     SMESH_TNodeXYZ pN = nextNodes[ iNotSame ];
4134     gp_XYZ extrDir( pN - pP ), faceNorm;
4135     SMESH_MeshAlgos::FaceNormal( face, faceNorm, /*normalized=*/false );
4136
4137     return faceNorm * extrDir < 0.0;
4138   }
4139
4140   //================================================================================
4141   /*!
4142    * \brief Assure that theElemSets[0] holds elements, not nodes
4143    */
4144   //================================================================================
4145
4146   void setElemsFirst( TIDSortedElemSet theElemSets[2] )
4147   {
4148     if ( !theElemSets[0].empty() &&
4149          (*theElemSets[0].begin())->GetType() == SMDSAbs_Node )
4150     {
4151       std::swap( theElemSets[0], theElemSets[1] );
4152     }
4153     else if ( !theElemSets[1].empty() &&
4154               (*theElemSets[1].begin())->GetType() != SMDSAbs_Node )
4155     {
4156       std::swap( theElemSets[0], theElemSets[1] );
4157     }
4158   }
4159 }
4160
4161 //=======================================================================
4162 /*!
4163  * \brief Create elements by sweeping an element
4164  * \param elem - element to sweep
4165  * \param newNodesItVec - nodes generated from each node of the element
4166  * \param newElems - generated elements
4167  * \param nbSteps - number of sweeping steps
4168  * \param srcElements - to append elem for each generated element
4169  */
4170 //=======================================================================
4171
4172 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement*               elem,
4173                                     const vector<TNodeOfNodeListMapItr> & newNodesItVec,
4174                                     list<const SMDS_MeshElement*>&        newElems,
4175                                     const size_t                          nbSteps,
4176                                     SMESH_SequenceOfElemPtr&              srcElements)
4177 {
4178   SMESHDS_Mesh* aMesh = GetMeshDS();
4179
4180   const int           nbNodes = elem->NbNodes();
4181   const int         nbCorners = elem->NbCornerNodes();
4182   SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
4183                                                           polyhedron creation !!! */
4184   // Loop on elem nodes:
4185   // find new nodes and detect same nodes indices
4186   vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
4187   vector<const SMDS_MeshNode*> prevNod( nbNodes );
4188   vector<const SMDS_MeshNode*> nextNod( nbNodes );
4189   vector<const SMDS_MeshNode*> midlNod( nbNodes );
4190
4191   int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
4192   vector<int> sames(nbNodes);
4193   vector<bool> isSingleNode(nbNodes);
4194
4195   for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4196     TNodeOfNodeListMapItr                        nnIt = newNodesItVec[ iNode ];
4197     const SMDS_MeshNode*                         node = nnIt->first;
4198     const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
4199     if ( listNewNodes.empty() )
4200       return;
4201
4202     itNN   [ iNode ] = listNewNodes.begin();
4203     prevNod[ iNode ] = node;
4204     nextNod[ iNode ] = listNewNodes.front();
4205
4206     isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
4207                                                              corner node of linear */
4208     if ( prevNod[ iNode ] != nextNod [ iNode ])
4209       nbDouble += !isSingleNode[iNode];
4210
4211     if( iNode < nbCorners ) { // check corners only
4212       if ( prevNod[ iNode ] == nextNod [ iNode ])
4213         sames[nbSame++] = iNode;
4214       else
4215         iNotSameNode = iNode;
4216     }
4217   }
4218
4219   if ( nbSame == nbNodes || nbSame > 2) {
4220     MESSAGE( " Too many same nodes of element " << elem->GetID() );
4221     return;
4222   }
4223
4224   if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
4225   {
4226     // fix nodes order to have bottom normal external
4227     if ( baseType == SMDSEntity_Polygon )
4228     {
4229       std::reverse( itNN.begin(), itNN.end() );
4230       std::reverse( prevNod.begin(), prevNod.end() );
4231       std::reverse( midlNod.begin(), midlNod.end() );
4232       std::reverse( nextNod.begin(), nextNod.end() );
4233       std::reverse( isSingleNode.begin(), isSingleNode.end() );
4234     }
4235     else
4236     {
4237       const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType, nbNodes );
4238       SMDS_MeshCell::applyInterlace( ind, itNN );
4239       SMDS_MeshCell::applyInterlace( ind, prevNod );
4240       SMDS_MeshCell::applyInterlace( ind, nextNod );
4241       SMDS_MeshCell::applyInterlace( ind, midlNod );
4242       SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4243       if ( nbSame > 0 )
4244       {
4245         sames[nbSame] = iNotSameNode;
4246         for ( int j = 0; j <= nbSame; ++j )
4247           for ( size_t i = 0; i < ind.size(); ++i )
4248             if ( ind[i] == sames[j] )
4249             {
4250               sames[j] = i;
4251               break;
4252             }
4253         iNotSameNode = sames[nbSame];
4254       }
4255     }
4256   }
4257   else if ( elem->GetType() == SMDSAbs_Edge )
4258   {
4259     // orient a new face same as adjacent one
4260     int i1, i2;
4261     const SMDS_MeshElement* e;
4262     TIDSortedElemSet dummy;
4263     if (( e = SMESH_MeshAlgos::FindFaceInSet( nextNod[0], prevNod[0], dummy,dummy, &i1, &i2 )) ||
4264         ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[1], nextNod[1], dummy,dummy, &i1, &i2 )) ||
4265         ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[0], prevNod[1], dummy,dummy, &i1, &i2 )))
4266     {
4267       // there is an adjacent face, check order of nodes in it
4268       bool sameOrder = ( Abs( i2 - i1 ) == 1 ) ? ( i2 > i1 ) : ( i2 < i1 );
4269       if ( sameOrder )
4270       {
4271         std::swap( itNN[0],    itNN[1] );
4272         std::swap( prevNod[0], prevNod[1] );
4273         std::swap( nextNod[0], nextNod[1] );
4274         std::swap( isSingleNode[0], isSingleNode[1] );
4275         if ( nbSame > 0 )
4276           sames[0] = 1 - sames[0];
4277         iNotSameNode = 1 - iNotSameNode;
4278       }
4279     }
4280   }
4281
4282   int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
4283   if ( nbSame > 0 ) {
4284     iSameNode    = sames[ nbSame-1 ];
4285     iBeforeSame  = ( iSameNode + nbCorners - 1 ) % nbCorners;
4286     iAfterSame   = ( iSameNode + 1 ) % nbCorners;
4287     iOpposSame   = ( iSameNode - 2 < 0  ? iSameNode + 2 : iSameNode - 2 );
4288   }
4289
4290   if ( baseType == SMDSEntity_Polygon )
4291   {
4292     if      ( nbNodes == 3 ) baseType = SMDSEntity_Triangle;
4293     else if ( nbNodes == 4 ) baseType = SMDSEntity_Quadrangle;
4294   }
4295   else if ( baseType == SMDSEntity_Quad_Polygon )
4296   {
4297     if      ( nbNodes == 6 ) baseType = SMDSEntity_Quad_Triangle;
4298     else if ( nbNodes == 8 ) baseType = SMDSEntity_Quad_Quadrangle;
4299   }
4300
4301   // make new elements
4302   for ( size_t iStep = 0; iStep < nbSteps; iStep++ )
4303   {
4304     // get next nodes
4305     for ( iNode = 0; iNode < nbNodes; iNode++ )
4306     {
4307       midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
4308       nextNod[ iNode ] = *itNN[ iNode ]++;
4309     }
4310
4311     SMDS_MeshElement* aNewElem = 0;
4312     /*if(!elem->IsPoly())*/ {
4313       switch ( baseType ) {
4314       case SMDSEntity_0D:
4315       case SMDSEntity_Node: { // sweep NODE
4316         if ( nbSame == 0 ) {
4317           if ( isSingleNode[0] )
4318             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
4319           else
4320             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
4321         }
4322         else
4323           return;
4324         break;
4325       }
4326       case SMDSEntity_Edge: { // sweep EDGE
4327         if ( nbDouble == 0 )
4328         {
4329           if ( nbSame == 0 ) // ---> quadrangle
4330             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4331                                       nextNod[ 1 ], nextNod[ 0 ] );
4332           else               // ---> triangle
4333             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4334                                       nextNod[ iNotSameNode ] );
4335         }
4336         else                 // ---> polygon
4337         {
4338           vector<const SMDS_MeshNode*> poly_nodes;
4339           poly_nodes.push_back( prevNod[0] );
4340           poly_nodes.push_back( prevNod[1] );
4341           if ( prevNod[1] != nextNod[1] )
4342           {
4343             if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
4344             poly_nodes.push_back( nextNod[1] );
4345           }
4346           if ( prevNod[0] != nextNod[0] )
4347           {
4348             poly_nodes.push_back( nextNod[0] );
4349             if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
4350           }
4351           switch ( poly_nodes.size() ) {
4352           case 3:
4353             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
4354             break;
4355           case 4:
4356             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
4357                                        poly_nodes[ 2 ], poly_nodes[ 3 ]);
4358             break;
4359           default:
4360             aNewElem = aMesh->AddPolygonalFace (poly_nodes);
4361           }
4362         }
4363         break;
4364       }
4365       case SMDSEntity_Triangle: // TRIANGLE --->
4366       {
4367         if ( nbDouble > 0 ) break;
4368         if ( nbSame == 0 )       // ---> pentahedron
4369           aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4370                                        nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
4371
4372         else if ( nbSame == 1 )  // ---> pyramid
4373           aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4374                                        nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
4375                                        nextNod[ iSameNode ]);
4376
4377         else // 2 same nodes:       ---> tetrahedron
4378           aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4379                                        nextNod[ iNotSameNode ]);
4380         break;
4381       }
4382       case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
4383       {
4384         if ( nbSame == 2 )
4385           return;
4386         if ( nbDouble+nbSame == 2 )
4387         {
4388           if(nbSame==0) {      // ---> quadratic quadrangle
4389             aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4390                                       prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
4391           }
4392           else { //(nbSame==1) // ---> quadratic triangle
4393             if(sames[0]==2) {
4394               return; // medium node on axis
4395             }
4396             else if(sames[0]==0)
4397               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1],
4398                                         prevNod[2], midlNod[1], nextNod[2] );
4399             else // sames[0]==1
4400               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[0],
4401                                         prevNod[2], nextNod[2], midlNod[0]);
4402           }
4403         }
4404         else if ( nbDouble == 3 )
4405         {
4406           if ( nbSame == 0 ) {  // ---> bi-quadratic quadrangle
4407             aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4408                                       prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
4409           }
4410         }
4411         else
4412           return;
4413         break;
4414       }
4415       case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
4416         if ( nbDouble > 0 ) break;
4417
4418         if ( nbSame == 0 )       // ---> hexahedron
4419           aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
4420                                        nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
4421
4422         else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
4423           aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4424                                        nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
4425                                        nextNod[ iSameNode ]);
4426           newElems.push_back( aNewElem );
4427           aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ],  prevNod[ iOpposSame ],
4428                                        prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
4429                                        nextNod[ iOpposSame ],  nextNod[ iBeforeSame ] );
4430         }
4431         else if ( nbSame == 2 ) { // ---> pentahedron
4432           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
4433             // iBeforeSame is same too
4434             aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
4435                                          nextNod[ iOpposSame ],  prevNod[ iSameNode ],
4436                                          prevNod[ iAfterSame ],  nextNod[ iAfterSame ]);
4437           else
4438             // iAfterSame is same too
4439             aNewElem = aMesh->AddVolume (prevNod[ iSameNode ],   prevNod[ iBeforeSame ],
4440                                          nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
4441                                          prevNod[ iOpposSame ],  nextNod[ iOpposSame ]);
4442         }
4443         break;
4444       }
4445       case SMDSEntity_Quad_Triangle:  // sweep (Bi)Quadratic TRIANGLE --->
4446       case SMDSEntity_BiQuad_Triangle: /* ??? */ {
4447         if ( nbDouble+nbSame != 3 ) break;
4448         if(nbSame==0) {
4449           // --->  pentahedron with 15 nodes
4450           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4451                                        nextNod[0], nextNod[1], nextNod[2],
4452                                        prevNod[3], prevNod[4], prevNod[5],
4453                                        nextNod[3], nextNod[4], nextNod[5],
4454                                        midlNod[0], midlNod[1], midlNod[2]);
4455         }
4456         else if(nbSame==1) {
4457           // --->  2d order pyramid of 13 nodes
4458           int apex = iSameNode;
4459           int i0 = ( apex + 1 ) % nbCorners;
4460           int i1 = ( apex - 1 + nbCorners ) % nbCorners;
4461           int i0a = apex + 3;
4462           int i1a = i1 + 3;
4463           int i01 = i0 + 3;
4464           aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
4465                                       nextNod[i0], nextNod[i1], prevNod[apex],
4466                                       prevNod[i01], midlNod[i0],
4467                                       nextNod[i01], midlNod[i1],
4468                                       prevNod[i1a], prevNod[i0a],
4469                                       nextNod[i0a], nextNod[i1a]);
4470         }
4471         else if(nbSame==2) {
4472           // --->  2d order tetrahedron of 10 nodes
4473           int n1 = iNotSameNode;
4474           int n2 = ( n1 + 1             ) % nbCorners;
4475           int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
4476           int n12 = n1 + 3;
4477           int n23 = n2 + 3;
4478           int n31 = n3 + 3;
4479           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
4480                                        prevNod[n12], prevNod[n23], prevNod[n31],
4481                                        midlNod[n1], nextNod[n12], nextNod[n31]);
4482         }
4483         break;
4484       }
4485       case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
4486         if( nbSame == 0 ) {
4487           if ( nbDouble != 4 ) break;
4488           // --->  hexahedron with 20 nodes
4489           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4490                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4491                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4492                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4493                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
4494         }
4495         else if(nbSame==1) {
4496           // ---> pyramid + pentahedron - can not be created since it is needed
4497           // additional middle node at the center of face
4498           //INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
4499           return;
4500         }
4501         else if( nbSame == 2 ) {
4502           if ( nbDouble != 2 ) break;
4503           // --->  2d order Pentahedron with 15 nodes
4504           int n1,n2,n4,n5;
4505           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
4506             // iBeforeSame is same too
4507             n1 = iBeforeSame;
4508             n2 = iOpposSame;
4509             n4 = iSameNode;
4510             n5 = iAfterSame;
4511           }
4512           else {
4513             // iAfterSame is same too
4514             n1 = iSameNode;
4515             n2 = iBeforeSame;
4516             n4 = iAfterSame;
4517             n5 = iOpposSame;
4518           }
4519           int n12 = n2 + 4;
4520           int n45 = n4 + 4;
4521           int n14 = n1 + 4;
4522           int n25 = n5 + 4;
4523           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
4524                                        prevNod[n4], prevNod[n5], nextNod[n5],
4525                                        prevNod[n12], midlNod[n2], nextNod[n12],
4526                                        prevNod[n45], midlNod[n5], nextNod[n45],
4527                                        prevNod[n14], prevNod[n25], nextNod[n25]);
4528         }
4529         break;
4530       }
4531       case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
4532
4533         if( nbSame == 0 && nbDouble == 9 ) {
4534           // --->  tri-quadratic hexahedron with 27 nodes
4535           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4536                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4537                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4538                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4539                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4540                                        prevNod[8], // bottom center
4541                                        midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4542                                        nextNod[8], // top center
4543                                        midlNod[8]);// elem center
4544         }
4545         else
4546         {
4547           return;
4548         }
4549         break;
4550       }
4551       case SMDSEntity_Polygon: { // sweep POLYGON
4552
4553         if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4554           // --->  hexagonal prism
4555           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4556                                        prevNod[3], prevNod[4], prevNod[5],
4557                                        nextNod[0], nextNod[1], nextNod[2],
4558                                        nextNod[3], nextNod[4], nextNod[5]);
4559         }
4560         break;
4561       }
4562       case SMDSEntity_Ball:
4563         return;
4564
4565       default:
4566         break;
4567       } // switch ( baseType )
4568     } // scope
4569
4570     if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4571     {
4572       if ( baseType != SMDSEntity_Polygon )
4573       {
4574         const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType,nbNodes);
4575         SMDS_MeshCell::applyInterlace( ind, prevNod );
4576         SMDS_MeshCell::applyInterlace( ind, nextNod );
4577         SMDS_MeshCell::applyInterlace( ind, midlNod );
4578         SMDS_MeshCell::applyInterlace( ind, itNN );
4579         SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4580         baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4581       }
4582       vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4583       vector<int> quantities (nbNodes + 2);
4584       polyedre_nodes.clear();
4585       quantities.clear();
4586
4587       // bottom of prism
4588       for (int inode = 0; inode < nbNodes; inode++)
4589         polyedre_nodes.push_back( prevNod[inode] );
4590       quantities.push_back( nbNodes );
4591
4592       // top of prism
4593       polyedre_nodes.push_back( nextNod[0] );
4594       for (int inode = nbNodes; inode-1; --inode )
4595         polyedre_nodes.push_back( nextNod[inode-1] );
4596       quantities.push_back( nbNodes );
4597
4598       // side faces
4599       // 3--6--2
4600       // |     |
4601       // 7     5
4602       // |     |
4603       // 0--4--1
4604       const int iQuad = elem->IsQuadratic();
4605       for (int iface = 0; iface < nbNodes; iface += 1+iQuad )
4606       {
4607         const int prevNbNodes = polyedre_nodes.size(); // to detect degenerated face
4608         int inextface = (iface+1+iQuad) % nbNodes;
4609         int imid      = (iface+1) % nbNodes;
4610         polyedre_nodes.push_back( prevNod[inextface] );         // 0
4611         if ( iQuad ) polyedre_nodes.push_back( prevNod[imid] ); // 4
4612         polyedre_nodes.push_back( prevNod[iface] );             // 1
4613         if ( prevNod[iface] != nextNod[iface] ) // 1 != 2
4614         {
4615           if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]); // 5
4616           polyedre_nodes.push_back( nextNod[iface] );                         // 2
4617         }
4618         if ( iQuad ) polyedre_nodes.push_back( nextNod[imid] );               // 6
4619         if ( prevNod[inextface] != nextNod[inextface] ) // 0 != 3
4620         {
4621           polyedre_nodes.push_back( nextNod[inextface] );                            // 3
4622           if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);// 7
4623         }
4624         const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4625         if ( nbFaceNodes > 2 )
4626           quantities.push_back( nbFaceNodes );
4627         else // degenerated face
4628           polyedre_nodes.resize( prevNbNodes );
4629       }
4630       aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4631
4632     } // try to create a polyherdal prism
4633
4634     if ( aNewElem ) {
4635       newElems.push_back( aNewElem );
4636       myLastCreatedElems.push_back(aNewElem);
4637       srcElements.push_back( elem );
4638     }
4639
4640     // set new prev nodes
4641     for ( iNode = 0; iNode < nbNodes; iNode++ )
4642       prevNod[ iNode ] = nextNod[ iNode ];
4643
4644   } // loop on steps
4645 }
4646
4647 //=======================================================================
4648 /*!
4649  * \brief Create 1D and 2D elements around swept elements
4650  * \param mapNewNodes - source nodes and ones generated from them
4651  * \param newElemsMap - source elements and ones generated from them
4652  * \param elemNewNodesMap - nodes generated from each node of each element
4653  * \param elemSet - all swept elements
4654  * \param nbSteps - number of sweeping steps
4655  * \param srcElements - to append elem for each generated element
4656  */
4657 //=======================================================================
4658
4659 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap &     mapNewNodes,
4660                                   TTElemOfElemListMap &    newElemsMap,
4661                                   TElemOfVecOfNnlmiMap &   elemNewNodesMap,
4662                                   TIDSortedElemSet&        elemSet,
4663                                   const int                nbSteps,
4664                                   SMESH_SequenceOfElemPtr& srcElements)
4665 {
4666   ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4667   SMESHDS_Mesh* aMesh = GetMeshDS();
4668
4669   // Find nodes belonging to only one initial element - sweep them into edges.
4670
4671   TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4672   for ( ; nList != mapNewNodes.end(); nList++ )
4673   {
4674     const SMDS_MeshNode* node =
4675       static_cast<const SMDS_MeshNode*>( nList->first );
4676     if ( newElemsMap.count( node ))
4677       continue; // node was extruded into edge
4678     SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4679     int nbInitElems = 0;
4680     const SMDS_MeshElement* el = 0;
4681     SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4682     while ( eIt->more() && nbInitElems < 2 ) {
4683       const SMDS_MeshElement* e = eIt->next();
4684       SMDSAbs_ElementType  type = e->GetType();
4685       if ( type == SMDSAbs_Volume ||
4686            type < highType ||
4687            !elemSet.count(e))
4688         continue;
4689       if ( type > highType ) {
4690         nbInitElems = 0;
4691         highType    = type;
4692       }
4693       el = e;
4694       ++nbInitElems;
4695     }
4696     if ( nbInitElems == 1 ) {
4697       bool NotCreateEdge = el && el->IsMediumNode(node);
4698       if(!NotCreateEdge) {
4699         vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
4700         list<const SMDS_MeshElement*> newEdges;
4701         sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
4702       }
4703     }
4704   }
4705
4706   // Make a ceiling for each element ie an equal element of last new nodes.
4707   // Find free links of faces - make edges and sweep them into faces.
4708
4709   ElemFeatures polyFace( SMDSAbs_Face, /*isPoly=*/true ), anyFace;
4710
4711   TTElemOfElemListMap::iterator  itElem      = newElemsMap.begin();
4712   TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
4713   for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
4714   {
4715     const SMDS_MeshElement* elem = itElem->first;
4716     vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
4717
4718     if(itElem->second.size()==0) continue;
4719
4720     const bool isQuadratic = elem->IsQuadratic();
4721
4722     if ( elem->GetType() == SMDSAbs_Edge ) {
4723       // create a ceiling edge
4724       if ( !isQuadratic ) {
4725         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4726                                vecNewNodes[ 1 ]->second.back())) {
4727           myLastCreatedElems.push_back(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4728                                                       vecNewNodes[ 1 ]->second.back()));
4729           srcElements.push_back( elem );
4730         }
4731       }
4732       else {
4733         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4734                                vecNewNodes[ 1 ]->second.back(),
4735                                vecNewNodes[ 2 ]->second.back())) {
4736           myLastCreatedElems.push_back(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4737                                                       vecNewNodes[ 1 ]->second.back(),
4738                                                       vecNewNodes[ 2 ]->second.back()));
4739           srcElements.push_back( elem );
4740         }
4741       }
4742     }
4743     if ( elem->GetType() != SMDSAbs_Face )
4744       continue;
4745
4746     bool hasFreeLinks = false;
4747
4748     TIDSortedElemSet avoidSet;
4749     avoidSet.insert( elem );
4750
4751     set<const SMDS_MeshNode*> aFaceLastNodes;
4752     int iNode, nbNodes = vecNewNodes.size();
4753     if ( !isQuadratic ) {
4754       // loop on the face nodes
4755       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4756         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4757         // look for free links of the face
4758         int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
4759         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4760         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4761         // check if a link n1-n2 is free
4762         if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
4763           hasFreeLinks = true;
4764           // make a new edge and a ceiling for a new edge
4765           const SMDS_MeshElement* edge;
4766           if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
4767             myLastCreatedElems.push_back( edge = aMesh->AddEdge( n1, n2 )); // free link edge
4768             srcElements.push_back( myLastCreatedElems.back() );
4769           }
4770           n1 = vecNewNodes[ iNode ]->second.back();
4771           n2 = vecNewNodes[ iNext ]->second.back();
4772           if ( !aMesh->FindEdge( n1, n2 )) {
4773             myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2 )); // new edge ceiling
4774             srcElements.push_back( edge );
4775           }
4776         }
4777       }
4778     }
4779     else { // elem is quadratic face
4780       int nbn = nbNodes/2;
4781       for ( iNode = 0; iNode < nbn; iNode++ ) {
4782         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4783         int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
4784         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4785         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4786         const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
4787         // check if a link is free
4788         if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
4789              ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
4790              ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
4791           hasFreeLinks = true;
4792           // make an edge and a ceiling for a new edge
4793           // find medium node
4794           if ( !aMesh->FindEdge( n1, n2, n3 )) {
4795             myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2, n3 )); // free link edge
4796             srcElements.push_back( elem );
4797           }
4798           n1 = vecNewNodes[ iNode ]->second.back();
4799           n2 = vecNewNodes[ iNext ]->second.back();
4800           n3 = vecNewNodes[ iNode+nbn ]->second.back();
4801           if ( !aMesh->FindEdge( n1, n2, n3 )) {
4802             myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
4803             srcElements.push_back( elem );
4804           }
4805         }
4806       }
4807       for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
4808         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4809       }
4810     }
4811
4812     // sweep free links into faces
4813
4814     if ( hasFreeLinks ) {
4815       list<const SMDS_MeshElement*> & newVolumes = itElem->second;
4816       int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
4817
4818       set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
4819       set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
4820       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4821         initNodeSet.insert( vecNewNodes[ iNode ]->first );
4822         topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
4823       }
4824       if ( isQuadratic && nbNodes % 2 ) {  // node set for the case of a biquadratic
4825         initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
4826         initNodeSetNoCenter.erase( vecNewNodes.back()->first );
4827       }
4828       for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
4829         list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
4830         std::advance( v, volNb );
4831         // find indices of free faces of a volume and their source edges
4832         list< int > freeInd;
4833         list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
4834         SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
4835         int iF, nbF = vTool.NbFaces();
4836         for ( iF = 0; iF < nbF; iF ++ ) {
4837           if (vTool.IsFreeFace( iF ) &&
4838               vTool.GetFaceNodes( iF, faceNodeSet ) &&
4839               initNodeSet != faceNodeSet) // except an initial face
4840           {
4841             if ( nbSteps == 1 && faceNodeSet == topNodeSet )
4842               continue;
4843             if ( faceNodeSet == initNodeSetNoCenter )
4844               continue;
4845             freeInd.push_back( iF );
4846             // find source edge of a free face iF
4847             vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
4848             vector<const SMDS_MeshNode*>::iterator lastCommom;
4849             commonNodes.resize( nbNodes, 0 );
4850             lastCommom = std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
4851                                                 initNodeSet.begin(), initNodeSet.end(),
4852                                                 commonNodes.begin());
4853             if ( std::distance( commonNodes.begin(), lastCommom ) == 3 )
4854               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
4855             else
4856               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
4857 #ifdef _DEBUG_
4858             if ( !srcEdges.back() )
4859             {
4860               cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
4861                    << iF << " of volume #" << vTool.ID() << endl;
4862             }
4863 #endif
4864           }
4865         }
4866         if ( freeInd.empty() )
4867           continue;
4868
4869         // create wall faces for all steps;
4870         // if such a face has been already created by sweep of edge,
4871         // assure that its orientation is OK
4872         for ( int iStep = 0; iStep < nbSteps; iStep++ )
4873         {
4874           vTool.Set( *v, /*ignoreCentralNodes=*/false );
4875           vTool.SetExternalNormal();
4876           const int nextShift = vTool.IsForward() ? +1 : -1;
4877           list< int >::iterator ind = freeInd.begin();
4878           list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
4879           for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
4880           {
4881             const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
4882             int nbn = vTool.NbFaceNodes( *ind );
4883             const SMDS_MeshElement * f = 0;
4884             if ( nbn == 3 )              ///// triangle
4885             {
4886               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
4887               if ( !f ||
4888                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4889               {
4890                 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
4891                                                      nodes[ 1 ],
4892                                                      nodes[ 1 + nextShift ] };
4893                 if ( f )
4894                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4895                 else
4896                   myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4897                                                                newOrder[ 2 ] ));
4898               }
4899             }
4900             else if ( nbn == 4 )       ///// quadrangle
4901             {
4902               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
4903               if ( !f ||
4904                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4905               {
4906                 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
4907                                                      nodes[ 2 ], nodes[ 2+nextShift ] };
4908                 if ( f )
4909                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4910                 else
4911                   myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4912                                                                newOrder[ 2 ], newOrder[ 3 ]));
4913               }
4914             }
4915             else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
4916             {
4917               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
4918               if ( !f ||
4919                    nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
4920               {
4921                 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
4922                                                      nodes[2],
4923                                                      nodes[2 + 2*nextShift],
4924                                                      nodes[3 - 2*nextShift],
4925                                                      nodes[3],
4926                                                      nodes[3 + 2*nextShift]};
4927                 if ( f )
4928                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4929                 else
4930                   myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ],
4931                                                                newOrder[ 1 ],
4932                                                                newOrder[ 2 ],
4933                                                                newOrder[ 3 ],
4934                                                                newOrder[ 4 ],
4935                                                                newOrder[ 5 ] ));
4936               }
4937             }
4938             else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
4939             {
4940               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
4941                                    nodes[1], nodes[3], nodes[5], nodes[7] );
4942               if ( !f ||
4943                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4944               {
4945                 const SMDS_MeshNode* newOrder[8] = { nodes[0],
4946                                                      nodes[4 - 2*nextShift],
4947                                                      nodes[4],
4948                                                      nodes[4 + 2*nextShift],
4949                                                      nodes[1],
4950                                                      nodes[5 - 2*nextShift],
4951                                                      nodes[5],
4952                                                      nodes[5 + 2*nextShift] };
4953                 if ( f )
4954                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4955                 else
4956                   myLastCreatedElems.push_back(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4957                                                               newOrder[ 2 ], newOrder[ 3 ],
4958                                                               newOrder[ 4 ], newOrder[ 5 ],
4959                                                               newOrder[ 6 ], newOrder[ 7 ]));
4960               }
4961             }
4962             else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
4963             {
4964               f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
4965                                       SMDSAbs_Face, /*noMedium=*/false);
4966               if ( !f ||
4967                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4968               {
4969                 const SMDS_MeshNode* newOrder[9] = { nodes[0],
4970                                                      nodes[4 - 2*nextShift],
4971                                                      nodes[4],
4972                                                      nodes[4 + 2*nextShift],
4973                                                      nodes[1],
4974                                                      nodes[5 - 2*nextShift],
4975                                                      nodes[5],
4976                                                      nodes[5 + 2*nextShift],
4977                                                      nodes[8] };
4978                 if ( f )
4979                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4980                 else
4981                   myLastCreatedElems.push_back(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4982                                                               newOrder[ 2 ], newOrder[ 3 ],
4983                                                               newOrder[ 4 ], newOrder[ 5 ],
4984                                                               newOrder[ 6 ], newOrder[ 7 ],
4985                                                               newOrder[ 8 ]));
4986               }
4987             }
4988             else  //////// polygon
4989             {
4990               vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
4991               const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
4992               if ( !f ||
4993                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
4994               {
4995                 if ( !vTool.IsForward() )
4996                   std::reverse( polygon_nodes.begin(), polygon_nodes.end());
4997                 if ( f )
4998                   aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
4999                 else
5000                   AddElement( polygon_nodes, polyFace.SetQuad( (*v)->IsQuadratic() ));
5001               }
5002             }
5003
5004             while ( srcElements.size() < myLastCreatedElems.size() )
5005               srcElements.push_back( *srcEdge );
5006
5007           }  // loop on free faces
5008
5009           // go to the next volume
5010           iVol = 0;
5011           while ( iVol++ < nbVolumesByStep ) v++;
5012
5013         } // loop on steps
5014       } // loop on volumes of one step
5015     } // sweep free links into faces
5016
5017     // Make a ceiling face with a normal external to a volume
5018
5019     // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
5020     SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
5021     int iF = lastVol.GetFaceIndex( aFaceLastNodes );
5022
5023     if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
5024       aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
5025       iF = lastVol.GetFaceIndex( aFaceLastNodes );
5026     }
5027     if ( iF >= 0 )
5028     {
5029       lastVol.SetExternalNormal();
5030       const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
5031       const               int nbn = lastVol.NbFaceNodes( iF );
5032       vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
5033       if ( !hasFreeLinks ||
5034            !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
5035       {
5036         const vector<int>& interlace =
5037           SMDS_MeshCell::interlacedSmdsOrder( elem->GetEntityType(), nbn );
5038         SMDS_MeshCell::applyInterlaceRev( interlace, nodeVec );
5039
5040         AddElement( nodeVec, anyFace.Init( elem ));
5041
5042         while ( srcElements.size() < myLastCreatedElems.size() )
5043           srcElements.push_back( elem );
5044       }
5045     }
5046   } // loop on swept elements
5047 }
5048
5049 //=======================================================================
5050 //function : RotationSweep
5051 //purpose  :
5052 //=======================================================================
5053
5054 SMESH_MeshEditor::PGroupIDs
5055 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet   theElemSets[2],
5056                                 const gp_Ax1&      theAxis,
5057                                 const double       theAngle,
5058                                 const int          theNbSteps,
5059                                 const double       theTol,
5060                                 const bool         theMakeGroups,
5061                                 const bool         theMakeWalls)
5062 {
5063   ClearLastCreated();
5064
5065   setElemsFirst( theElemSets );
5066   myLastCreatedElems.reserve( theElemSets[0].size() * theNbSteps );
5067   myLastCreatedNodes.reserve( theElemSets[1].size() * theNbSteps );
5068
5069   // source elements for each generated one
5070   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5071   srcElems.reserve( theElemSets[0].size() );
5072   srcNodes.reserve( theElemSets[1].size() );
5073
5074   gp_Trsf aTrsf;
5075   aTrsf.SetRotation( theAxis, theAngle );
5076   gp_Trsf aTrsf2;
5077   aTrsf2.SetRotation( theAxis, theAngle/2. );
5078
5079   gp_Lin aLine( theAxis );
5080   double aSqTol = theTol * theTol;
5081
5082   SMESHDS_Mesh* aMesh = GetMeshDS();
5083
5084   TNodeOfNodeListMap mapNewNodes;
5085   TElemOfVecOfNnlmiMap mapElemNewNodes;
5086   TTElemOfElemListMap newElemsMap;
5087
5088   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5089                                      myMesh->NbFaces(ORDER_QUADRATIC) +
5090                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
5091   // loop on theElemSets
5092   TIDSortedElemSet::iterator itElem;
5093   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5094   {
5095     TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5096     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5097       const SMDS_MeshElement* elem = *itElem;
5098       if ( !elem || elem->GetType() == SMDSAbs_Volume )
5099         continue;
5100       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5101       newNodesItVec.reserve( elem->NbNodes() );
5102
5103       // loop on elem nodes
5104       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5105       while ( itN->more() )
5106       {
5107         const SMDS_MeshNode* node = cast2Node( itN->next() );
5108
5109         gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
5110         double coord[3];
5111         aXYZ.Coord( coord[0], coord[1], coord[2] );
5112         bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
5113
5114         // check if a node has been already sweeped
5115         TNodeOfNodeListMapItr nIt =
5116           mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5117         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5118         if ( listNewNodes.empty() )
5119         {
5120           // check if we are to create medium nodes between corner ones
5121           bool needMediumNodes = false;
5122           if ( isQuadraticMesh )
5123           {
5124             SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5125             while (it->more() && !needMediumNodes )
5126             {
5127               const SMDS_MeshElement* invElem = it->next();
5128               if ( invElem != elem && !theElems.count( invElem )) continue;
5129               needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5130               if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5131                 needMediumNodes = true;
5132             }
5133           }
5134
5135           // make new nodes
5136           const SMDS_MeshNode * newNode = node;
5137           for ( int i = 0; i < theNbSteps; i++ ) {
5138             if ( !isOnAxis ) {
5139               if ( needMediumNodes )  // create a medium node
5140               {
5141                 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5142                 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5143                 myLastCreatedNodes.push_back(newNode);
5144                 srcNodes.push_back( node );
5145                 listNewNodes.push_back( newNode );
5146                 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5147               }
5148               else {
5149                 aTrsf.Transforms( coord[0], coord[1], coord[2] );
5150               }
5151               // create a corner node
5152               newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5153               myLastCreatedNodes.push_back(newNode);
5154               srcNodes.push_back( node );
5155               listNewNodes.push_back( newNode );
5156             }
5157             else {
5158               listNewNodes.push_back( newNode );
5159               // if ( needMediumNodes )
5160               //   listNewNodes.push_back( newNode );
5161             }
5162           }
5163         }
5164         newNodesItVec.push_back( nIt );
5165       }
5166       // make new elements
5167       sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
5168     }
5169   }
5170
5171   if ( theMakeWalls )
5172     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], theNbSteps, srcElems );
5173
5174   PGroupIDs newGroupIDs;
5175   if ( theMakeGroups )
5176     newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
5177
5178   return newGroupIDs;
5179 }
5180
5181 //=======================================================================
5182 //function : ExtrusParam
5183 //purpose  : standard construction
5184 //=======================================================================
5185
5186 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Vec&            theStep,
5187                                             const int                theNbSteps,
5188                                             const std::list<double>& theScales,
5189                                             const gp_XYZ*            theBasePoint,
5190                                             const int                theFlags,
5191                                             const double             theTolerance):
5192   myDir( theStep ),
5193   myBaseP( Precision::Infinite(), 0, 0 ),
5194   myFlags( theFlags ),
5195   myTolerance( theTolerance ),
5196   myElemsToUse( NULL )
5197 {
5198   mySteps = new TColStd_HSequenceOfReal;
5199   const double stepSize = theStep.Magnitude();
5200   for (int i=1; i<=theNbSteps; i++ )
5201     mySteps->Append( stepSize );
5202
5203   int nbScales = theScales.size();
5204   if ( nbScales > 0 )
5205   {
5206     if ( IsLinearVariation() && nbScales < theNbSteps )
5207     {
5208       myScales.reserve( theNbSteps );
5209       std::list<double>::const_iterator scale = theScales.begin();
5210       double prevScale = 1.0;
5211       for ( int iSc = 1; scale != theScales.end(); ++scale, ++iSc )
5212       {
5213         int      iStep = int( iSc / double( nbScales ) * theNbSteps + 0.5 );
5214         int    stDelta = Max( 1, iStep - myScales.size());
5215         double scDelta = ( *scale - prevScale ) / stDelta;
5216         for ( int iStep = 0; iStep < stDelta; ++iStep )
5217         {
5218           myScales.push_back( prevScale + scDelta );
5219           prevScale = myScales.back();
5220         }
5221         prevScale = *scale;
5222       }
5223     }
5224     else
5225     {
5226       myScales.assign( theScales.begin(), theScales.end() );
5227     }
5228   }
5229   if ( theBasePoint )
5230   {
5231     myBaseP = *theBasePoint;
5232   }
5233
5234   if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5235       ( theTolerance > 0 ))
5236   {
5237     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5238   }
5239   else
5240   {
5241     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5242   }
5243 }
5244
5245 //=======================================================================
5246 //function : ExtrusParam
5247 //purpose  : steps are given explicitly
5248 //=======================================================================
5249
5250 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Dir&                   theDir,
5251                                             Handle(TColStd_HSequenceOfReal) theSteps,
5252                                             const int                       theFlags,
5253                                             const double                    theTolerance):
5254   myDir( theDir ),
5255   mySteps( theSteps ),
5256   myFlags( theFlags ),
5257   myTolerance( theTolerance ),
5258   myElemsToUse( NULL )
5259 {
5260   if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5261       ( theTolerance > 0 ))
5262   {
5263     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5264   }
5265   else
5266   {
5267     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5268   }
5269 }
5270
5271 //=======================================================================
5272 //function : ExtrusParam
5273 //purpose  : for extrusion by normal
5274 //=======================================================================
5275
5276 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const double theStepSize,
5277                                             const int    theNbSteps,
5278                                             const int    theFlags,
5279                                             const int    theDim ):
5280   myDir( 1,0,0 ),
5281   mySteps( new TColStd_HSequenceOfReal ),
5282   myFlags( theFlags ),
5283   myTolerance( 0 ),
5284   myElemsToUse( NULL )
5285 {
5286   for (int i = 0; i < theNbSteps; i++ )
5287     mySteps->Append( theStepSize );
5288
5289   if ( theDim == 1 )
5290   {
5291     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal1D;
5292   }
5293   else
5294   {
5295     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal2D;
5296   }
5297 }
5298
5299 //=======================================================================
5300 //function : ExtrusParam::SetElementsToUse
5301 //purpose  : stores elements to use for extrusion by normal, depending on
5302 //           state of EXTRUSION_FLAG_USE_INPUT_ELEMS_ONLY flag;
5303 //           define myBaseP for scaling
5304 //=======================================================================
5305
5306 void SMESH_MeshEditor::ExtrusParam::SetElementsToUse( const TIDSortedElemSet& elems,
5307                                                       const TIDSortedElemSet& nodes )
5308 {
5309   myElemsToUse = ToUseInpElemsOnly() ? & elems : 0;
5310
5311   if ( Precision::IsInfinite( myBaseP.X() )) // myBaseP not defined
5312   {
5313     myBaseP.SetCoord( 0.,0.,0. );
5314     TIDSortedElemSet newNodes;
5315
5316     const TIDSortedElemSet* elemSets[] = { &elems, &nodes };
5317     for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5318     {
5319       const TIDSortedElemSet& elements = *( elemSets[ is2ndSet ]);
5320       TIDSortedElemSet::const_iterator itElem = elements.begin();
5321       for ( ; itElem != elements.end(); itElem++ )
5322       {
5323         const SMDS_MeshElement* elem = *itElem;
5324         SMDS_ElemIteratorPtr     itN = elem->nodesIterator();
5325         while ( itN->more() ) {
5326           const SMDS_MeshElement* node = itN->next();
5327           if ( newNodes.insert( node ).second )
5328             myBaseP += SMESH_TNodeXYZ( node );
5329         }
5330       }
5331     }
5332     myBaseP /= newNodes.size();
5333   }
5334 }
5335
5336 //=======================================================================
5337 //function : ExtrusParam::beginStepIter
5338 //purpose  : prepare iteration on steps
5339 //=======================================================================
5340
5341 void SMESH_MeshEditor::ExtrusParam::beginStepIter( bool withMediumNodes )
5342 {
5343   myWithMediumNodes = withMediumNodes;
5344   myNextStep = 1;
5345   myCurSteps.clear();
5346 }
5347 //=======================================================================
5348 //function : ExtrusParam::moreSteps
5349 //purpose  : are there more steps?
5350 //=======================================================================
5351
5352 bool SMESH_MeshEditor::ExtrusParam::moreSteps()
5353 {
5354   return myNextStep <= mySteps->Length() || !myCurSteps.empty();
5355 }
5356 //=======================================================================
5357 //function : ExtrusParam::nextStep
5358 //purpose  : returns the next step
5359 //=======================================================================
5360
5361 double SMESH_MeshEditor::ExtrusParam::nextStep()
5362 {
5363   double res = 0;
5364   if ( !myCurSteps.empty() )
5365   {
5366     res = myCurSteps.back();
5367     myCurSteps.pop_back();
5368   }
5369   else if ( myNextStep <= mySteps->Length() )
5370   {
5371     myCurSteps.push_back( mySteps->Value( myNextStep ));
5372     ++myNextStep;
5373     if ( myWithMediumNodes )
5374     {
5375       myCurSteps.back() /= 2.;
5376       myCurSteps.push_back( myCurSteps.back() );
5377     }
5378     res = nextStep();
5379   }
5380   return res;
5381 }
5382
5383 //=======================================================================
5384 //function : ExtrusParam::makeNodesByDir
5385 //purpose  : create nodes for standard extrusion
5386 //=======================================================================
5387
5388 int SMESH_MeshEditor::ExtrusParam::
5389 makeNodesByDir( SMESHDS_Mesh*                     mesh,
5390                 const SMDS_MeshNode*              srcNode,
5391                 std::list<const SMDS_MeshNode*> & newNodes,
5392                 const bool                        makeMediumNodes)
5393 {
5394   gp_XYZ p = SMESH_TNodeXYZ( srcNode );
5395
5396   int nbNodes = 0;
5397   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5398   {
5399     p += myDir.XYZ() * nextStep();
5400     const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5401     newNodes.push_back( newNode );
5402   }
5403
5404   if ( !myScales.empty() )
5405   {
5406     if ( makeMediumNodes && myMediumScales.empty() )
5407     {
5408       myMediumScales.resize( myScales.size() );
5409       double prevFactor = 1.;
5410       for ( size_t i = 0; i < myScales.size(); ++i )
5411       {
5412         myMediumScales[i] = 0.5 * ( prevFactor + myScales[i] );
5413         prevFactor = myScales[i];
5414       }
5415     }
5416     typedef std::vector<double>::iterator ScaleIt;
5417     ScaleIt scales[] = { myScales.begin(), myMediumScales.begin() };
5418
5419     size_t iSc = 0, nbScales = myScales.size() + myMediumScales.size();
5420
5421     gp_XYZ center = myBaseP;
5422     std::list<const SMDS_MeshNode*>::iterator nIt = newNodes.begin();
5423     size_t iN  = 0;
5424     for ( beginStepIter( makeMediumNodes ); moreSteps() && ( iN < nbScales ); ++nIt, ++iN )
5425     {
5426       center += myDir.XYZ() * nextStep();
5427
5428       iSc += int( makeMediumNodes );
5429       ScaleIt& scale = scales[ iSc % 2 ];
5430       
5431       gp_XYZ xyz = SMESH_TNodeXYZ( *nIt );
5432       xyz = ( *scale * ( xyz - center )) + center;
5433       mesh->MoveNode( *nIt, xyz.X(), xyz.Y(), xyz.Z() );
5434
5435       ++scale;
5436     }
5437   }
5438   return nbNodes;
5439 }
5440
5441 //=======================================================================
5442 //function : ExtrusParam::makeNodesByDirAndSew
5443 //purpose  : create nodes for standard extrusion with sewing
5444 //=======================================================================
5445
5446 int SMESH_MeshEditor::ExtrusParam::
5447 makeNodesByDirAndSew( SMESHDS_Mesh*                     mesh,
5448                       const SMDS_MeshNode*              srcNode,
5449                       std::list<const SMDS_MeshNode*> & newNodes,
5450                       const bool                        makeMediumNodes)
5451 {
5452   gp_XYZ P1 = SMESH_TNodeXYZ( srcNode );
5453
5454   int nbNodes = 0;
5455   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5456   {
5457     P1 += myDir.XYZ() * nextStep();
5458
5459     // try to search in sequence of existing nodes
5460     // if myNodes.size()>0 we 'nave to use given sequence
5461     // else - use all nodes of mesh
5462     const SMDS_MeshNode * node = 0;
5463     if ( myNodes.Length() > 0 ) {
5464       int i;
5465       for ( i = 1; i <= myNodes.Length(); i++ ) {
5466         gp_XYZ P2 = SMESH_TNodeXYZ( myNodes.Value(i) );
5467         if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5468         {
5469           node = myNodes.Value(i);
5470           break;
5471         }
5472       }
5473     }
5474     else {
5475       SMDS_NodeIteratorPtr itn = mesh->nodesIterator();
5476       while(itn->more()) {
5477         SMESH_TNodeXYZ P2( itn->next() );
5478         if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5479         {
5480           node = P2._node;
5481           break;
5482         }
5483       }
5484     }
5485
5486     if ( !node )
5487       node = mesh->AddNode( P1.X(), P1.Y(), P1.Z() );
5488
5489     newNodes.push_back( node );
5490
5491   } // loop on steps
5492
5493   return nbNodes;
5494 }
5495
5496 //=======================================================================
5497 //function : ExtrusParam::makeNodesByNormal2D
5498 //purpose  : create nodes for extrusion using normals of faces
5499 //=======================================================================
5500
5501 int SMESH_MeshEditor::ExtrusParam::
5502 makeNodesByNormal2D( SMESHDS_Mesh*                     mesh,
5503                      const SMDS_MeshNode*              srcNode,
5504                      std::list<const SMDS_MeshNode*> & newNodes,
5505                      const bool                        makeMediumNodes)
5506 {
5507   const bool alongAvgNorm = ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL );
5508
5509   gp_XYZ p = SMESH_TNodeXYZ( srcNode );
5510
5511   // get normals to faces sharing srcNode
5512   vector< gp_XYZ > norms, baryCenters;
5513   gp_XYZ norm, avgNorm( 0,0,0 );
5514   SMDS_ElemIteratorPtr faceIt = srcNode->GetInverseElementIterator( SMDSAbs_Face );
5515   while ( faceIt->more() )
5516   {
5517     const SMDS_MeshElement* face = faceIt->next();
5518     if ( myElemsToUse && !myElemsToUse->count( face ))
5519       continue;
5520     if ( SMESH_MeshAlgos::FaceNormal( face, norm, /*normalized=*/true ))
5521     {
5522       norms.push_back( norm );
5523       avgNorm += norm;
5524       if ( !alongAvgNorm )
5525       {
5526         gp_XYZ bc(0,0,0);
5527         int nbN = 0;
5528         for ( SMDS_ElemIteratorPtr nIt = face->nodesIterator(); nIt->more(); ++nbN )
5529           bc += SMESH_TNodeXYZ( nIt->next() );
5530         baryCenters.push_back( bc / nbN );
5531       }
5532     }
5533   }
5534
5535   if ( norms.empty() ) return 0;
5536
5537   double normSize = avgNorm.Modulus();
5538   if ( normSize < std::numeric_limits<double>::min() )
5539     return 0;
5540
5541   if ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL ) // extrude along avgNorm
5542   {
5543     myDir = avgNorm;
5544     return makeNodesByDir( mesh, srcNode, newNodes, makeMediumNodes );
5545   }
5546
5547   avgNorm /= normSize;
5548
5549   int nbNodes = 0;
5550   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5551   {
5552     gp_XYZ pNew = p;
5553     double stepSize = nextStep();
5554
5555     if ( norms.size() > 1 )
5556     {
5557       for ( size_t iF = 0; iF < norms.size(); ++iF ) // loop on faces
5558       {
5559         // translate plane of a face
5560         baryCenters[ iF ] += norms[ iF ] * stepSize;
5561
5562         // find point of intersection of the face plane located at baryCenters[ iF ]
5563         // and avgNorm located at pNew
5564         double d    = -( norms[ iF ] * baryCenters[ iF ]); // d of plane equation ax+by+cz+d=0
5565         double dot  = ( norms[ iF ] * avgNorm );
5566         if ( dot < std::numeric_limits<double>::min() )
5567           dot = stepSize * 1e-3;
5568         double step = -( norms[ iF ] * pNew + d ) / dot;
5569         pNew += step * avgNorm;
5570       }
5571     }
5572     else
5573     {
5574       pNew += stepSize * avgNorm;
5575     }
5576     p = pNew;
5577
5578     const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5579     newNodes.push_back( newNode );
5580   }
5581   return nbNodes;
5582 }
5583
5584 //=======================================================================
5585 //function : ExtrusParam::makeNodesByNormal1D
5586 //purpose  : create nodes for extrusion using normals of edges
5587 //=======================================================================
5588
5589 int SMESH_MeshEditor::ExtrusParam::
5590 makeNodesByNormal1D( SMESHDS_Mesh*                     mesh,
5591                      const SMDS_MeshNode*              srcNode,
5592                      std::list<const SMDS_MeshNode*> & newNodes,
5593                      const bool                        makeMediumNodes)
5594 {
5595   throw SALOME_Exception("Extrusion 1D by Normal not implemented");
5596   return 0;
5597 }
5598
5599 //=======================================================================
5600 //function : ExtrusionSweep
5601 //purpose  :
5602 //=======================================================================
5603
5604 SMESH_MeshEditor::PGroupIDs
5605 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet     theElems[2],
5606                                   const gp_Vec&        theStep,
5607                                   const int            theNbSteps,
5608                                   TTElemOfElemListMap& newElemsMap,
5609                                   const int            theFlags,
5610                                   const double         theTolerance)
5611 {
5612   ExtrusParam aParams( theStep, theNbSteps, std::list<double>(), 0, theFlags, theTolerance );
5613   return ExtrusionSweep( theElems, aParams, newElemsMap );
5614 }
5615
5616
5617 //=======================================================================
5618 //function : ExtrusionSweep
5619 //purpose  :
5620 //=======================================================================
5621
5622 SMESH_MeshEditor::PGroupIDs
5623 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet     theElemSets[2],
5624                                   ExtrusParam&         theParams,
5625                                   TTElemOfElemListMap& newElemsMap)
5626 {
5627   ClearLastCreated();
5628
5629   setElemsFirst( theElemSets );
5630   myLastCreatedElems.reserve( theElemSets[0].size() * theParams.NbSteps() );
5631   myLastCreatedNodes.reserve( theElemSets[1].size() * theParams.NbSteps() );
5632
5633   // source elements for each generated one
5634   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5635   srcElems.reserve( theElemSets[0].size() );
5636   srcNodes.reserve( theElemSets[1].size() );
5637
5638   const int nbSteps = theParams.NbSteps();
5639   theParams.SetElementsToUse( theElemSets[0], theElemSets[1] );
5640
5641   TNodeOfNodeListMap   mapNewNodes;
5642   TElemOfVecOfNnlmiMap mapElemNewNodes;
5643
5644   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5645                                      myMesh->NbFaces(ORDER_QUADRATIC) +
5646                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
5647   // loop on theElems
5648   TIDSortedElemSet::iterator itElem;
5649   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5650   {
5651     TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5652     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5653     {
5654       // check element type
5655       const SMDS_MeshElement* elem = *itElem;
5656       if ( !elem  || elem->GetType() == SMDSAbs_Volume )
5657         continue;
5658
5659       const size_t nbNodes = elem->NbNodes();
5660       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5661       newNodesItVec.reserve( nbNodes );
5662
5663       // loop on elem nodes
5664       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5665       while ( itN->more() )
5666       {
5667         // check if a node has been already sweeped
5668         const SMDS_MeshNode* node = cast2Node( itN->next() );
5669         TNodeOfNodeListMap::iterator nIt =
5670           mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5671         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5672         if ( listNewNodes.empty() )
5673         {
5674           // make new nodes
5675
5676           // check if we are to create medium nodes between corner ones
5677           bool needMediumNodes = false;
5678           if ( isQuadraticMesh )
5679           {
5680             SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5681             while (it->more() && !needMediumNodes )
5682             {
5683               const SMDS_MeshElement* invElem = it->next();
5684               if ( invElem != elem && !theElems.count( invElem )) continue;
5685               needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5686               if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5687                 needMediumNodes = true;
5688             }
5689           }
5690           // create nodes for all steps
5691           if ( theParams.MakeNodes( GetMeshDS(), node, listNewNodes, needMediumNodes ))
5692           {
5693             list<const SMDS_MeshNode*>::iterator newNodesIt = listNewNodes.begin();
5694             for ( ; newNodesIt != listNewNodes.end(); ++newNodesIt )
5695             {
5696               myLastCreatedNodes.push_back( *newNodesIt );
5697               srcNodes.push_back( node );
5698             }
5699           }
5700           else
5701           {
5702             break; // newNodesItVec will be shorter than nbNodes
5703           }
5704         }
5705         newNodesItVec.push_back( nIt );
5706       }
5707       // make new elements
5708       if ( newNodesItVec.size() == nbNodes )
5709         sweepElement( elem, newNodesItVec, newElemsMap[elem], nbSteps, srcElems );
5710     }
5711   }
5712
5713   if ( theParams.ToMakeBoundary() ) {
5714     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], nbSteps, srcElems );
5715   }
5716   PGroupIDs newGroupIDs;
5717   if ( theParams.ToMakeGroups() )
5718     newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
5719
5720   return newGroupIDs;
5721 }
5722
5723 //=======================================================================
5724 //function : ExtrusionAlongTrack
5725 //purpose  :
5726 //=======================================================================
5727 SMESH_MeshEditor::Extrusion_Error
5728 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet     theElements[2],
5729                                        SMESH_subMesh*       theTrack,
5730                                        const SMDS_MeshNode* theN1,
5731                                        const bool           theHasAngles,
5732                                        list<double>&        theAngles,
5733                                        const bool           theLinearVariation,
5734                                        const bool           theHasRefPoint,
5735                                        const gp_Pnt&        theRefPoint,
5736                                        const bool           theMakeGroups)
5737 {
5738   ClearLastCreated();
5739
5740   int aNbE;
5741   std::list<double> aPrms;
5742   TIDSortedElemSet::iterator itElem;
5743
5744   gp_XYZ aGC;
5745   TopoDS_Edge aTrackEdge;
5746   TopoDS_Vertex aV1, aV2;
5747
5748   SMDS_ElemIteratorPtr aItE;
5749   SMDS_NodeIteratorPtr aItN;
5750   SMDSAbs_ElementType aTypeE;
5751
5752   TNodeOfNodeListMap mapNewNodes;
5753
5754   // 1. Check data
5755   aNbE = theElements[0].size() + theElements[1].size();
5756   // nothing to do
5757   if ( !aNbE )
5758     return EXTR_NO_ELEMENTS;
5759
5760   // 1.1 Track Pattern
5761   ASSERT( theTrack );
5762
5763   SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS();
5764   if ( !pSubMeshDS )
5765     return ExtrusionAlongTrack( theElements, theTrack->GetFather(), theN1,
5766                                 theHasAngles, theAngles, theLinearVariation,
5767                                 theHasRefPoint, theRefPoint, theMakeGroups );
5768
5769   aItE = pSubMeshDS->GetElements();
5770   while ( aItE->more() ) {
5771     const SMDS_MeshElement* pE = aItE->next();
5772     aTypeE = pE->GetType();
5773     // Pattern must contain links only
5774     if ( aTypeE != SMDSAbs_Edge )
5775       return EXTR_PATH_NOT_EDGE;
5776   }
5777
5778   list<SMESH_MeshEditor_PathPoint> fullList;
5779
5780   const TopoDS_Shape& aS = theTrack->GetSubShape();
5781   // Sub-shape for the Pattern must be an Edge or Wire
5782   if( aS.ShapeType() == TopAbs_EDGE ) {
5783     aTrackEdge = TopoDS::Edge( aS );
5784     // the Edge must not be degenerated
5785     if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
5786       return EXTR_BAD_PATH_SHAPE;
5787     TopExp::Vertices( aTrackEdge, aV1, aV2 );
5788     aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
5789     const SMDS_MeshNode* aN1 = aItN->next();
5790     aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
5791     const SMDS_MeshNode* aN2 = aItN->next();
5792     // starting node must be aN1 or aN2
5793     if ( !( aN1 == theN1 || aN2 == theN1 ) )
5794       return EXTR_BAD_STARTING_NODE;
5795     aItN = pSubMeshDS->GetNodes();
5796     while ( aItN->more() ) {
5797       const SMDS_MeshNode* pNode = aItN->next();
5798       const SMDS_EdgePosition* pEPos =
5799         static_cast<const SMDS_EdgePosition*>( 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           const SMDS_EdgePosition* pEPos =
5843             static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5844           double aT = pEPos->GetUParameter();
5845           aPrms.push_back( aT );
5846         }
5847         list<SMESH_MeshEditor_PathPoint> LPP;
5848         //Extrusion_Error err =
5849         makeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
5850         LLPPs.push_back(LPP);
5851         UsedNums.Add(k);
5852         // update startN for search following edge
5853         if( aN1->GetID() == startNid ) startNid = aN2->GetID();
5854         else startNid = aN1->GetID();
5855         break;
5856       }
5857     }
5858     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5859     list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5860     list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5861     for(; itPP!=firstList.end(); itPP++) {
5862       fullList.push_back( *itPP );
5863     }
5864     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5865     fullList.pop_back();
5866     itLLPP++;
5867     for(; itLLPP!=LLPPs.end(); itLLPP++) {
5868       list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5869       itPP = currList.begin();
5870       SMESH_MeshEditor_PathPoint PP2 = currList.front();
5871       gp_Dir D1 = PP1.Tangent();
5872       gp_Dir D2 = PP2.Tangent();
5873       gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
5874                            (D1.Z()+D2.Z())/2 ) );
5875       PP1.SetTangent(Dnew);
5876       fullList.push_back(PP1);
5877       itPP++;
5878       for(; itPP!=firstList.end(); itPP++) {
5879         fullList.push_back( *itPP );
5880       }
5881       PP1 = fullList.back();
5882       fullList.pop_back();
5883     }
5884     // if wire not closed
5885     fullList.push_back(PP1);
5886     // else ???
5887   }
5888   else {
5889     return EXTR_BAD_PATH_SHAPE;
5890   }
5891
5892   return makeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
5893                           theHasRefPoint, theRefPoint, theMakeGroups);
5894 }
5895
5896
5897 //=======================================================================
5898 //function : ExtrusionAlongTrack
5899 //purpose  :
5900 //=======================================================================
5901 SMESH_MeshEditor::Extrusion_Error
5902 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet     theElements[2],
5903                                        SMESH_Mesh*          theTrack,
5904                                        const SMDS_MeshNode* theN1,
5905                                        const bool           theHasAngles,
5906                                        list<double>&        theAngles,
5907                                        const bool           theLinearVariation,
5908                                        const bool           theHasRefPoint,
5909                                        const gp_Pnt&        theRefPoint,
5910                                        const bool           theMakeGroups)
5911 {
5912   ClearLastCreated();
5913
5914   int aNbE;
5915   std::list<double> aPrms;
5916   TIDSortedElemSet::iterator itElem;
5917
5918   gp_XYZ aGC;
5919   TopoDS_Edge aTrackEdge;
5920   TopoDS_Vertex aV1, aV2;
5921
5922   SMDS_ElemIteratorPtr aItE;
5923   SMDS_NodeIteratorPtr aItN;
5924   SMDSAbs_ElementType aTypeE;
5925
5926   TNodeOfNodeListMap mapNewNodes;
5927
5928   // 1. Check data
5929   aNbE = theElements[0].size() + theElements[1].size();
5930   // nothing to do
5931   if ( !aNbE )
5932     return EXTR_NO_ELEMENTS;
5933
5934   // 1.1 Track Pattern
5935   ASSERT( theTrack );
5936
5937   SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS();
5938
5939   aItE = pMeshDS->elementsIterator();
5940   while ( aItE->more() ) {
5941     const SMDS_MeshElement* pE = aItE->next();
5942     aTypeE = pE->GetType();
5943     // Pattern must contain links only
5944     if ( aTypeE != SMDSAbs_Edge )
5945       return EXTR_PATH_NOT_EDGE;
5946   }
5947
5948   list<SMESH_MeshEditor_PathPoint> fullList;
5949
5950   const TopoDS_Shape& aS = theTrack->GetShapeToMesh();
5951
5952   if ( !theTrack->HasShapeToMesh() ) {
5953     //Mesh without shape
5954     const SMDS_MeshNode* currentNode = NULL;
5955     const SMDS_MeshNode* prevNode = theN1;
5956     std::vector<const SMDS_MeshNode*> aNodesList;
5957     aNodesList.push_back(theN1);
5958     int nbEdges = 0, conn=0;
5959     const SMDS_MeshElement* prevElem = NULL;
5960     const SMDS_MeshElement* currentElem = NULL;
5961     int totalNbEdges = theTrack->NbEdges();
5962     SMDS_ElemIteratorPtr nIt;
5963
5964     //check start node
5965     if( !theTrack->GetMeshDS()->Contains(theN1) ) {
5966       return EXTR_BAD_STARTING_NODE;
5967     }
5968
5969     conn = nbEdgeConnectivity(theN1);
5970     if( conn != 1 )
5971       return EXTR_PATH_NOT_EDGE;
5972
5973     aItE = theN1->GetInverseElementIterator();
5974     prevElem = aItE->next();
5975     currentElem = prevElem;
5976     //Get all nodes
5977     if(totalNbEdges == 1 ) {
5978       nIt = currentElem->nodesIterator();
5979       currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5980       if(currentNode == prevNode)
5981         currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5982       aNodesList.push_back(currentNode);
5983     } else {
5984       nIt = currentElem->nodesIterator();
5985       while( nIt->more() ) {
5986         currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5987         if(currentNode == prevNode)
5988           currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5989         aNodesList.push_back(currentNode);
5990
5991         //case of the closed mesh
5992         if(currentNode == theN1) {
5993           nbEdges++;
5994           break;
5995         }
5996
5997         conn = nbEdgeConnectivity(currentNode);
5998         if(conn > 2) {
5999           return EXTR_PATH_NOT_EDGE;
6000         }else if( conn == 1 && nbEdges > 0 ) {
6001           //End of the path
6002           nbEdges++;
6003           break;
6004         }else {
6005           prevNode = currentNode;
6006           aItE = currentNode->GetInverseElementIterator();
6007           currentElem = aItE->next();
6008           if( currentElem  == prevElem)
6009             currentElem = aItE->next();
6010           nIt = currentElem->nodesIterator();
6011           prevElem = currentElem;
6012           nbEdges++;
6013         }
6014       }
6015     }
6016
6017     if(nbEdges != totalNbEdges)
6018       return EXTR_PATH_NOT_EDGE;
6019
6020     TopTools_SequenceOfShape Edges;
6021     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6022     int startNid = theN1->GetID();
6023     for ( size_t i = 1; i < aNodesList.size(); i++ )
6024     {
6025       gp_Pnt     p1 = SMESH_TNodeXYZ( aNodesList[i-1] );
6026       gp_Pnt     p2 = SMESH_TNodeXYZ( aNodesList[i] );
6027       TopoDS_Edge e = BRepBuilderAPI_MakeEdge( p1, p2 );
6028       list<SMESH_MeshEditor_PathPoint> LPP;
6029       aPrms.clear();
6030       makeEdgePathPoints(aPrms, e, (aNodesList[i-1]->GetID()==startNid), LPP);
6031       LLPPs.push_back(LPP);
6032       if ( aNodesList[i-1]->GetID() == startNid ) startNid = aNodesList[i  ]->GetID();
6033       else                                        startNid = aNodesList[i-1]->GetID();
6034     }
6035
6036     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6037     list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
6038     list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
6039     for(; itPP!=firstList.end(); itPP++) {
6040       fullList.push_back( *itPP );
6041     }
6042
6043     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6044     SMESH_MeshEditor_PathPoint PP2;
6045     fullList.pop_back();
6046     itLLPP++;
6047     for(; itLLPP!=LLPPs.end(); itLLPP++) {
6048       list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
6049       itPP = currList.begin();
6050       PP2 = currList.front();
6051       gp_Dir D1 = PP1.Tangent();
6052       gp_Dir D2 = PP2.Tangent();
6053       gp_Dir Dnew( 0.5 * ( D1.XYZ() + D2.XYZ() ));
6054       PP1.SetTangent(Dnew);
6055       fullList.push_back(PP1);
6056       itPP++;
6057       for(; itPP!=currList.end(); itPP++) {
6058         fullList.push_back( *itPP );
6059       }
6060       PP1 = fullList.back();
6061       fullList.pop_back();
6062     }
6063     fullList.push_back(PP1);
6064
6065   } // Sub-shape for the Pattern must be an Edge or Wire
6066   else if ( aS.ShapeType() == TopAbs_EDGE )
6067   {
6068     aTrackEdge = TopoDS::Edge( aS );
6069     // the Edge must not be degenerated
6070     if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
6071       return EXTR_BAD_PATH_SHAPE;
6072     TopExp::Vertices( aTrackEdge, aV1, aV2 );
6073     const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6074     const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6075     // starting node must be aN1 or aN2
6076     if ( !( aN1 == theN1 || aN2 == theN1 ) )
6077       return EXTR_BAD_STARTING_NODE;
6078     aItN = pMeshDS->nodesIterator();
6079     while ( aItN->more() ) {
6080       const SMDS_MeshNode* pNode = aItN->next();
6081       if( pNode==aN1 || pNode==aN2 ) continue;
6082       const SMDS_EdgePosition* pEPos =
6083         static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6084       double aT = pEPos->GetUParameter();
6085       aPrms.push_back( aT );
6086     }
6087     //Extrusion_Error err =
6088     makeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
6089   }
6090   else if( aS.ShapeType() == TopAbs_WIRE ) {
6091     list< SMESH_subMesh* > LSM;
6092     TopTools_SequenceOfShape Edges;
6093     TopExp_Explorer eExp(aS, TopAbs_EDGE);
6094     for(; eExp.More(); eExp.Next()) {
6095       TopoDS_Edge E = TopoDS::Edge( eExp.Current() );
6096       if( SMESH_Algo::isDegenerated(E) ) continue;
6097       SMESH_subMesh* SM = theTrack->GetSubMesh(E);
6098       if(SM) {
6099         LSM.push_back(SM);
6100         Edges.Append(E);
6101       }
6102     }
6103     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6104     TopoDS_Vertex aVprev;
6105     TColStd_MapOfInteger UsedNums;
6106     int NbEdges = Edges.Length();
6107     int i = 1;
6108     for(; i<=NbEdges; i++) {
6109       int k = 0;
6110       list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
6111       for(; itLSM!=LSM.end(); itLSM++) {
6112         k++;
6113         if(UsedNums.Contains(k)) continue;
6114         aTrackEdge = TopoDS::Edge( Edges.Value(k) );
6115         SMESH_subMesh* locTrack = *itLSM;
6116         SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
6117         TopExp::Vertices( aTrackEdge, aV1, aV2 );
6118         bool aN1isOK = false, aN2isOK = false;
6119         if ( aVprev.IsNull() ) {
6120           // if previous vertex is not yet defined, it means that we in the beginning of wire
6121           // and we have to find initial vertex corresponding to starting node theN1
6122           const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6123           const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6124           // starting node must be aN1 or aN2
6125           aN1isOK = ( aN1 && aN1 == theN1 );
6126           aN2isOK = ( aN2 && aN2 == theN1 );
6127         }
6128         else {
6129           // we have specified ending vertex of the previous edge on the previous iteration
6130           // and we have just to check that it corresponds to any vertex in current segment
6131           aN1isOK = aVprev.IsSame( aV1 );
6132           aN2isOK = aVprev.IsSame( aV2 );
6133         }
6134         if ( !aN1isOK && !aN2isOK ) continue;
6135         // 2. Collect parameters on the track edge
6136         aPrms.clear();
6137         aItN = locMeshDS->GetNodes();
6138         while ( aItN->more() ) {
6139           const SMDS_MeshNode*     pNode = aItN->next();
6140           const SMDS_EdgePosition* pEPos =
6141             static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6142           double aT = pEPos->GetUParameter();
6143           aPrms.push_back( aT );
6144         }
6145         list<SMESH_MeshEditor_PathPoint> LPP;
6146         //Extrusion_Error err =
6147         makeEdgePathPoints(aPrms, aTrackEdge, aN1isOK, LPP);
6148         LLPPs.push_back(LPP);
6149         UsedNums.Add(k);
6150         // update startN for search following edge
6151         if ( aN1isOK ) aVprev = aV2;
6152         else           aVprev = aV1;
6153         break;
6154       }
6155     }
6156     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6157     list<SMESH_MeshEditor_PathPoint>& firstList = *itLLPP;
6158     fullList.splice( fullList.end(), firstList );
6159
6160     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6161     fullList.pop_back();
6162     itLLPP++;
6163     for(; itLLPP!=LLPPs.end(); itLLPP++) {
6164       list<SMESH_MeshEditor_PathPoint>& currList = *itLLPP;
6165       SMESH_MeshEditor_PathPoint PP2 = currList.front();
6166       gp_Dir D1 = PP1.Tangent();
6167       gp_Dir D2 = PP2.Tangent();
6168       gp_Dir Dnew( D1.XYZ() + D2.XYZ() );
6169       PP1.SetTangent(Dnew);
6170       fullList.push_back(PP1);
6171       fullList.splice( fullList.end(), currList, ++currList.begin(), currList.end() );
6172       PP1 = fullList.back();
6173       fullList.pop_back();
6174     }
6175     // if wire not closed
6176     fullList.push_back(PP1);
6177     // else ???
6178   }
6179   else {
6180     return EXTR_BAD_PATH_SHAPE;
6181   }
6182
6183   return makeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
6184                           theHasRefPoint, theRefPoint, theMakeGroups);
6185 }
6186
6187
6188 //=======================================================================
6189 //function : makeEdgePathPoints
6190 //purpose  : auxiliary for ExtrusionAlongTrack
6191 //=======================================================================
6192 SMESH_MeshEditor::Extrusion_Error
6193 SMESH_MeshEditor::makeEdgePathPoints(std::list<double>&                aPrms,
6194                                      const TopoDS_Edge&                aTrackEdge,
6195                                      bool                              FirstIsStart,
6196                                      list<SMESH_MeshEditor_PathPoint>& LPP)
6197 {
6198   Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2;
6199   aTolVec=1.e-7;
6200   aTolVec2=aTolVec*aTolVec;
6201   double aT1, aT2;
6202   TopoDS_Vertex aV1, aV2;
6203   TopExp::Vertices( aTrackEdge, aV1, aV2 );
6204   aT1=BRep_Tool::Parameter( aV1, aTrackEdge );
6205   aT2=BRep_Tool::Parameter( aV2, aTrackEdge );
6206   // 2. Collect parameters on the track edge
6207   aPrms.push_front( aT1 );
6208   aPrms.push_back( aT2 );
6209   // sort parameters
6210   aPrms.sort();
6211   if( FirstIsStart ) {
6212     if ( aT1 > aT2 ) {
6213       aPrms.reverse();
6214     }
6215   }
6216   else {
6217     if ( aT2 > aT1 ) {
6218       aPrms.reverse();
6219     }
6220   }
6221   // 3. Path Points
6222   SMESH_MeshEditor_PathPoint aPP;
6223   Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 );
6224   std::list<double>::iterator aItD = aPrms.begin();
6225   for(; aItD != aPrms.end(); ++aItD) {
6226     double aT = *aItD;
6227     gp_Pnt aP3D;
6228     gp_Vec aVec;
6229     aC3D->D1( aT, aP3D, aVec );
6230     aL2 = aVec.SquareMagnitude();
6231     if ( aL2 < aTolVec2 )
6232       return EXTR_CANT_GET_TANGENT;
6233     gp_Dir aTgt( FirstIsStart ? aVec : -aVec );
6234     aPP.SetPnt( aP3D );
6235     aPP.SetTangent( aTgt );
6236     aPP.SetParameter( aT );
6237     LPP.push_back(aPP);
6238   }
6239   return EXTR_OK;
6240 }
6241
6242
6243 //=======================================================================
6244 //function : makeExtrElements
6245 //purpose  : auxiliary for ExtrusionAlongTrack
6246 //=======================================================================
6247 SMESH_MeshEditor::Extrusion_Error
6248 SMESH_MeshEditor::makeExtrElements(TIDSortedElemSet                  theElemSets[2],
6249                                    list<SMESH_MeshEditor_PathPoint>& fullList,
6250                                    const bool                        theHasAngles,
6251                                    list<double>&                     theAngles,
6252                                    const bool                        theLinearVariation,
6253                                    const bool                        theHasRefPoint,
6254                                    const gp_Pnt&                     theRefPoint,
6255                                    const bool                        theMakeGroups)
6256 {
6257   const int aNbTP = fullList.size();
6258
6259   // Angles
6260   if( theHasAngles && !theAngles.empty() && theLinearVariation )
6261     linearAngleVariation(aNbTP-1, theAngles);
6262
6263   // fill vector of path points with angles
6264   vector<SMESH_MeshEditor_PathPoint> aPPs;
6265   list<SMESH_MeshEditor_PathPoint>::iterator itPP = fullList.begin();
6266   list<double>::iterator                 itAngles = theAngles.begin();
6267   aPPs.push_back( *itPP++ );
6268   for( ; itPP != fullList.end(); itPP++) {
6269     aPPs.push_back( *itPP );
6270     if ( theHasAngles && itAngles != theAngles.end() )
6271       aPPs.back().SetAngle( *itAngles++ );
6272   }
6273
6274   TNodeOfNodeListMap   mapNewNodes;
6275   TElemOfVecOfNnlmiMap mapElemNewNodes;
6276   TTElemOfElemListMap  newElemsMap;
6277   TIDSortedElemSet::iterator itElem;
6278   // source elements for each generated one
6279   SMESH_SequenceOfElemPtr srcElems, srcNodes;
6280
6281   // 3. Center of rotation aV0
6282   gp_Pnt aV0 = theRefPoint;
6283   if ( !theHasRefPoint )
6284   {
6285     gp_XYZ aGC( 0.,0.,0. );
6286     TIDSortedElemSet newNodes;
6287
6288     for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6289     {
6290       TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6291       itElem = theElements.begin();
6292       for ( ; itElem != theElements.end(); itElem++ )
6293       {
6294         const SMDS_MeshElement* elem = *itElem;
6295         SMDS_ElemIteratorPtr     itN = elem->nodesIterator();
6296         while ( itN->more() ) {
6297           const SMDS_MeshElement* node = itN->next();
6298           if ( newNodes.insert( node ).second )
6299             aGC += SMESH_TNodeXYZ( node );
6300         }
6301       }
6302     }
6303     aGC /= newNodes.size();
6304     aV0.SetXYZ( aGC );
6305   } // if (!theHasRefPoint) {
6306
6307   // 4. Processing the elements
6308   SMESHDS_Mesh* aMesh = GetMeshDS();
6309   list<const SMDS_MeshNode*> emptyList;
6310
6311   setElemsFirst( theElemSets );
6312   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6313   {
6314     TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6315     for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ )
6316     {
6317       const SMDS_MeshElement* elem = *itElem;
6318
6319       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
6320       newNodesItVec.reserve( elem->NbNodes() );
6321
6322       // loop on elem nodes
6323       int nodeIndex = -1;
6324       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6325       while ( itN->more() )
6326       {
6327         ++nodeIndex;
6328         // check if a node has been already processed
6329         const SMDS_MeshNode* node = cast2Node( itN->next() );
6330         TNodeOfNodeListMap::iterator nIt = mapNewNodes.insert( make_pair( node, emptyList )).first;
6331         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
6332         if ( listNewNodes.empty() )
6333         {
6334           // make new nodes
6335           Standard_Real aAngle1x, aAngleT1T0, aTolAng;
6336           gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
6337           gp_Ax1 anAx1, anAxT1T0;
6338           gp_Dir aDT1x, aDT0x, aDT1T0;
6339
6340           aTolAng=1.e-4;
6341
6342           aV0x = aV0;
6343           aPN0 = SMESH_TNodeXYZ( node );
6344
6345           const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
6346           aP0x = aPP0.Pnt();
6347           aDT0x= aPP0.Tangent();
6348
6349           for ( int j = 1; j < aNbTP; ++j ) {
6350             const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
6351             aP1x     = aPP1.Pnt();
6352             aDT1x    = aPP1.Tangent();
6353             aAngle1x = aPP1.Angle();
6354
6355             gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
6356             // Translation
6357             gp_Vec aV01x( aP0x, aP1x );
6358             aTrsf.SetTranslation( aV01x );
6359
6360             // traslated point
6361             aV1x = aV0x.Transformed( aTrsf );
6362             aPN1 = aPN0.Transformed( aTrsf );
6363
6364             // rotation 1 [ T1,T0 ]
6365             aAngleT1T0=-aDT1x.Angle( aDT0x );
6366             if (fabs(aAngleT1T0) > aTolAng)
6367             {
6368               aDT1T0=aDT1x^aDT0x;
6369               anAxT1T0.SetLocation( aV1x );
6370               anAxT1T0.SetDirection( aDT1T0 );
6371               aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
6372
6373               aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
6374             }
6375
6376             // rotation 2
6377             if ( theHasAngles ) {
6378               anAx1.SetLocation( aV1x );
6379               anAx1.SetDirection( aDT1x );
6380               aTrsfRot.SetRotation( anAx1, aAngle1x );
6381
6382               aPN1 = aPN1.Transformed( aTrsfRot );
6383             }
6384
6385             // make new node
6386             if ( elem->IsQuadratic() && !elem->IsMediumNode(node) )
6387             {
6388               // create additional node
6389               gp_XYZ midP = 0.5 * ( aPN1.XYZ() + aPN0.XYZ() );
6390               const SMDS_MeshNode* newNode = aMesh->AddNode( midP.X(), midP.Y(), midP.Z() );
6391               myLastCreatedNodes.push_back(newNode);
6392               srcNodes.push_back( node );
6393               listNewNodes.push_back( newNode );
6394             }
6395             const SMDS_MeshNode* newNode = aMesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
6396             myLastCreatedNodes.push_back(newNode);
6397             srcNodes.push_back( node );
6398             listNewNodes.push_back( newNode );
6399
6400             aPN0 = aPN1;
6401             aP0x = aP1x;
6402             aV0x = aV1x;
6403             aDT0x = aDT1x;
6404           }
6405         }
6406         else if( elem->IsQuadratic() && !elem->IsMediumNode(node) )
6407         {
6408           // if current elem is quadratic and current node is not medium
6409           // we have to check - may be it is needed to insert additional nodes
6410           list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
6411           if ((int) listNewNodes.size() == aNbTP-1 )
6412           {
6413             vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
6414             gp_XYZ P(node->X(), node->Y(), node->Z());
6415             list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
6416             int i;
6417             for(i=0; i<aNbTP-1; i++) {
6418               const SMDS_MeshNode* N = *it;
6419               double x = ( N->X() + P.X() )/2.;
6420               double y = ( N->Y() + P.Y() )/2.;
6421               double z = ( N->Z() + P.Z() )/2.;
6422               const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
6423               srcNodes.push_back( node );
6424               myLastCreatedNodes.push_back(newN);
6425               aNodes[2*i] = newN;
6426               aNodes[2*i+1] = N;
6427               P = gp_XYZ(N->X(),N->Y(),N->Z());
6428             }
6429             listNewNodes.clear();
6430             for(i=0; i<2*(aNbTP-1); i++) {
6431               listNewNodes.push_back(aNodes[i]);
6432             }
6433           }
6434         }
6435
6436         newNodesItVec.push_back( nIt );
6437       }
6438
6439       // make new elements
6440       sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
6441     }
6442   }
6443
6444   makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], aNbTP-1, srcElems );
6445
6446   if ( theMakeGroups )
6447     generateGroups( srcNodes, srcElems, "extruded");
6448
6449   return EXTR_OK;
6450 }
6451
6452
6453 //=======================================================================
6454 //function : linearAngleVariation
6455 //purpose  : spread values over nbSteps
6456 //=======================================================================
6457
6458 void SMESH_MeshEditor::linearAngleVariation(const int     nbSteps,
6459                                             list<double>& Angles)
6460 {
6461   int nbAngles = Angles.size();
6462   if( nbSteps > nbAngles && nbAngles > 0 )
6463   {
6464     vector<double> theAngles(nbAngles);
6465     theAngles.assign( Angles.begin(), Angles.end() );
6466
6467     list<double> res;
6468     double rAn2St = double( nbAngles ) / double( nbSteps );
6469     double angPrev = 0, angle;
6470     for ( int iSt = 0; iSt < nbSteps; ++iSt )
6471     {
6472       double angCur = rAn2St * ( iSt+1 );
6473       double angCurFloor  = floor( angCur );
6474       double angPrevFloor = floor( angPrev );
6475       if ( angPrevFloor == angCurFloor )
6476         angle = rAn2St * theAngles[ int( angCurFloor ) ];
6477       else {
6478         int iP = int( angPrevFloor );
6479         double angPrevCeil = ceil(angPrev);
6480         angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6481
6482         int iC = int( angCurFloor );
6483         if ( iC < nbAngles )
6484           angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6485
6486         iP = int( angPrevCeil );
6487         while ( iC-- > iP )
6488           angle += theAngles[ iC ];
6489       }
6490       res.push_back(angle);
6491       angPrev = angCur;
6492     }
6493     Angles.swap( res );
6494   }
6495 }
6496
6497
6498 //================================================================================
6499 /*!
6500  * \brief Move or copy theElements applying theTrsf to their nodes
6501  *  \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6502  *  \param theTrsf - transformation to apply
6503  *  \param theCopy - if true, create translated copies of theElems
6504  *  \param theMakeGroups - if true and theCopy, create translated groups
6505  *  \param theTargetMesh - mesh to copy translated elements into
6506  *  \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6507  */
6508 //================================================================================
6509
6510 SMESH_MeshEditor::PGroupIDs
6511 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6512                              const gp_Trsf&     theTrsf,
6513                              const bool         theCopy,
6514                              const bool         theMakeGroups,
6515                              SMESH_Mesh*        theTargetMesh)
6516 {
6517   ClearLastCreated();
6518   myLastCreatedElems.reserve( theElems.size() );
6519
6520   bool needReverse = false;
6521   string groupPostfix;
6522   switch ( theTrsf.Form() ) {
6523   case gp_PntMirror:
6524     needReverse = true;
6525     groupPostfix = "mirrored";
6526     break;
6527   case gp_Ax1Mirror:
6528     groupPostfix = "mirrored";
6529     break;
6530   case gp_Ax2Mirror:
6531     needReverse = true;
6532     groupPostfix = "mirrored";
6533     break;
6534   case gp_Rotation:
6535     groupPostfix = "rotated";
6536     break;
6537   case gp_Translation:
6538     groupPostfix = "translated";
6539     break;
6540   case gp_Scale:
6541     groupPostfix = "scaled";
6542     break;
6543   case gp_CompoundTrsf: // different scale by axis
6544     groupPostfix = "scaled";
6545     break;
6546   default:
6547     needReverse = false;
6548     groupPostfix = "transformed";
6549   }
6550
6551   SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6552   SMESHDS_Mesh* aMesh    = GetMeshDS();
6553
6554   SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6555   SMESH_MeshEditor* editor = theTargetMesh ? & targetMeshEditor : theCopy ? this : 0;
6556   SMESH_MeshEditor::ElemFeatures elemType;
6557
6558   // map old node to new one
6559   TNodeNodeMap nodeMap;
6560
6561   // elements sharing moved nodes; those of them which have all
6562   // nodes mirrored but are not in theElems are to be reversed
6563   TIDSortedElemSet inverseElemSet;
6564
6565   // source elements for each generated one
6566   SMESH_SequenceOfElemPtr srcElems, srcNodes;
6567
6568   // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6569   TIDSortedElemSet orphanNode;
6570
6571   if ( theElems.empty() ) // transform the whole mesh
6572   {
6573     // add all elements
6574     SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6575     while ( eIt->more() ) theElems.insert( eIt->next() );
6576     // add orphan nodes
6577     SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6578     while ( nIt->more() )
6579     {
6580       const SMDS_MeshNode* node = nIt->next();
6581       if ( node->NbInverseElements() == 0)
6582         orphanNode.insert( node );
6583     }
6584   }
6585
6586   // loop on elements to transform nodes : first orphan nodes then elems
6587   TIDSortedElemSet::iterator itElem;
6588   TIDSortedElemSet *elements[] = { &orphanNode, &theElems };
6589   for (int i=0; i<2; i++)
6590     for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ )
6591     {
6592       const SMDS_MeshElement* elem = *itElem;
6593       if ( !elem )
6594         continue;
6595
6596       // loop on elem nodes
6597       double coord[3];
6598       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6599       while ( itN->more() )
6600       {
6601         const SMDS_MeshNode* node = cast2Node( itN->next() );
6602         // check if a node has been already transformed
6603         pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6604           nodeMap.insert( make_pair ( node, node ));
6605         if ( !n2n_isnew.second )
6606           continue;
6607
6608         node->GetXYZ( coord );
6609         theTrsf.Transforms( coord[0], coord[1], coord[2] );
6610         if ( theTargetMesh ) {
6611           const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6612           n2n_isnew.first->second = newNode;
6613           myLastCreatedNodes.push_back(newNode);
6614           srcNodes.push_back( node );
6615         }
6616         else if ( theCopy ) {
6617           const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6618           n2n_isnew.first->second = newNode;
6619           myLastCreatedNodes.push_back(newNode);
6620           srcNodes.push_back( node );
6621         }
6622         else {
6623           aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6624           // node position on shape becomes invalid
6625           const_cast< SMDS_MeshNode* > ( node )->SetPosition
6626             ( SMDS_SpacePosition::originSpacePosition() );
6627         }
6628
6629         // keep inverse elements
6630         if ( !theCopy && !theTargetMesh && needReverse ) {
6631           SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6632           while ( invElemIt->more() ) {
6633             const SMDS_MeshElement* iel = invElemIt->next();
6634             inverseElemSet.insert( iel );
6635           }
6636         }
6637       }
6638     } // loop on elems in { &orphanNode, &theElems };
6639
6640   // either create new elements or reverse mirrored ones
6641   if ( !theCopy && !needReverse && !theTargetMesh )
6642     return PGroupIDs();
6643
6644   theElems.insert( inverseElemSet.begin(),inverseElemSet.end() );
6645
6646   // Replicate or reverse elements
6647
6648   std::vector<int> iForw;
6649   vector<const SMDS_MeshNode*> nodes;
6650   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6651   {
6652     const SMDS_MeshElement* elem = *itElem;
6653     if ( !elem ) continue;
6654
6655     SMDSAbs_GeometryType geomType = elem->GetGeomType();
6656     size_t               nbNodes  = elem->NbNodes();
6657     if ( geomType == SMDSGeom_NONE ) continue; // node
6658
6659     nodes.resize( nbNodes );
6660
6661     if ( geomType == SMDSGeom_POLYHEDRA )  // ------------------ polyhedral volume
6662     {
6663       const SMDS_VtkVolume* aPolyedre = dynamic_cast<const SMDS_VtkVolume*>( elem );
6664       if (!aPolyedre)
6665         continue;
6666       nodes.clear();
6667       bool allTransformed = true;
6668       int nbFaces = aPolyedre->NbFaces();
6669       for (int iface = 1; iface <= nbFaces && allTransformed; iface++)
6670       {
6671         int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6672         for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++)
6673         {
6674           const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6675           TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6676           if ( nodeMapIt == nodeMap.end() )
6677             allTransformed = false; // not all nodes transformed
6678           else
6679             nodes.push_back((*nodeMapIt).second);
6680         }
6681         if ( needReverse && allTransformed )
6682           std::reverse( nodes.end() - nbFaceNodes, nodes.end() );
6683       }
6684       if ( !allTransformed )
6685         continue; // not all nodes transformed
6686     }
6687     else // ----------------------- the rest element types
6688     {
6689       while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6690       const vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType(), nbNodes );
6691       const vector<int>&    i = needReverse ? iRev : iForw;
6692
6693       // find transformed nodes
6694       size_t iNode = 0;
6695       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6696       while ( itN->more() ) {
6697         const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
6698         TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6699         if ( nodeMapIt == nodeMap.end() )
6700           break; // not all nodes transformed
6701         nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
6702       }
6703       if ( iNode != nbNodes )
6704         continue; // not all nodes transformed
6705     }
6706
6707     if ( editor ) {
6708       // copy in this or a new mesh
6709       if ( editor->AddElement( nodes, elemType.Init( elem, /*basicOnly=*/false )))
6710         srcElems.push_back( elem );
6711     }
6712     else {
6713       // reverse element as it was reversed by transformation
6714       if ( nbNodes > 2 )
6715         aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
6716     }
6717
6718   } // loop on elements
6719
6720   if ( editor && editor != this )
6721     myLastCreatedElems.swap( editor->myLastCreatedElems );
6722
6723   PGroupIDs newGroupIDs;
6724
6725   if ( ( theMakeGroups && theCopy ) ||
6726        ( theMakeGroups && theTargetMesh ) )
6727     newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh, false );
6728
6729   return newGroupIDs;
6730 }
6731
6732 //================================================================================
6733 /*!
6734  * \brief Make an offset mesh from a source 2D mesh
6735  *  \param [in] theElements - source faces
6736  *  \param [in] theValue - offset value
6737  *  \param [out] theTgtMesh - a mesh to add offset elements to
6738  *  \param [in] theMakeGroups - to generate groups
6739  *  \return PGroupIDs - IDs of created groups
6740  */
6741 //================================================================================
6742
6743 SMESH_MeshEditor::PGroupIDs SMESH_MeshEditor::Offset( TIDSortedElemSet & theElements,
6744                                                       const double       theValue,
6745                                                       SMESH_Mesh*        theTgtMesh,
6746                                                       const bool         theMakeGroups,
6747                                                       const bool         theFixSelfIntersection)
6748 {
6749   SMESHDS_Mesh*    meshDS = GetMeshDS();
6750   SMESHDS_Mesh* tgtMeshDS = theTgtMesh->GetMeshDS();
6751   SMESH_MeshEditor tgtEditor( theTgtMesh );
6752
6753   SMDS_ElemIteratorPtr eIt;
6754   if ( theElements.empty() ) eIt = meshDS->elementsIterator( SMDSAbs_Face );
6755   else                       eIt = SMESHUtils::elemSetIterator( theElements );
6756
6757   SMESH_MeshAlgos::TEPairVec new2OldFaces;
6758   SMESH_MeshAlgos::TNPairVec new2OldNodes;
6759   std::unique_ptr< SMDS_Mesh > offsetMesh
6760     ( SMESH_MeshAlgos::MakeOffset( eIt, *meshDS, theValue,
6761                                    theFixSelfIntersection,
6762                                    new2OldFaces, new2OldNodes ));
6763
6764   offsetMesh->Modified();
6765   offsetMesh->CompactMesh(); // make IDs start from 1
6766
6767   // source elements for each generated one
6768   SMESH_SequenceOfElemPtr srcElems, srcNodes;
6769   srcElems.reserve( new2OldFaces.size() );
6770   srcNodes.reserve( new2OldNodes.size() );
6771
6772   ClearLastCreated();
6773   myLastCreatedElems.reserve( new2OldFaces.size() );
6774   myLastCreatedNodes.reserve( new2OldNodes.size() );
6775
6776   // copy offsetMesh to theTgtMesh
6777
6778   int idShift = meshDS->MaxNodeID();
6779   for ( size_t i = 0; i < new2OldNodes.size(); ++i )
6780     if ( const SMDS_MeshNode* n = new2OldNodes[ i ].first )
6781     {
6782       if ( n->NbInverseElements() > 0 )
6783       {
6784         const SMDS_MeshNode* n2 =
6785           tgtMeshDS->AddNodeWithID( n->X(), n->Y(), n->Z(), idShift + n->GetID() );
6786         myLastCreatedNodes.push_back( n2 );
6787         srcNodes.push_back( new2OldNodes[ i ].second );
6788       }
6789     }
6790
6791   ElemFeatures elemType;
6792   for ( size_t i = 0; i < new2OldFaces.size(); ++i )
6793     if ( const SMDS_MeshElement* f = new2OldFaces[ i ].first )
6794     {
6795       elemType.Init( f );
6796       elemType.myNodes.clear();
6797       for ( SMDS_NodeIteratorPtr nIt = f->nodeIterator(); nIt->more(); )
6798       {
6799         const SMDS_MeshNode* n2 = nIt->next();
6800         elemType.myNodes.push_back( tgtMeshDS->FindNode( idShift + n2->GetID() ));
6801       }
6802       tgtEditor.AddElement( elemType.myNodes, elemType );
6803       srcElems.push_back( new2OldFaces[ i ].second );
6804     }
6805
6806   myLastCreatedElems.swap( tgtEditor.myLastCreatedElems );
6807
6808   PGroupIDs newGroupIDs;
6809   if ( theMakeGroups )
6810     newGroupIDs = generateGroups( srcNodes, srcElems, "offset", theTgtMesh, false );
6811
6812   return newGroupIDs;
6813 }
6814
6815 //=======================================================================
6816 /*!
6817  * \brief Create groups of elements made during transformation
6818  *  \param nodeGens - nodes making corresponding myLastCreatedNodes
6819  *  \param elemGens - elements making corresponding myLastCreatedElems
6820  *  \param postfix - to push_back to names of new groups
6821  *  \param targetMesh - mesh to create groups in
6822  *  \param topPresent - is there are "top" elements that are created by sweeping
6823  */
6824 //=======================================================================
6825
6826 SMESH_MeshEditor::PGroupIDs
6827 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
6828                                  const SMESH_SequenceOfElemPtr& elemGens,
6829                                  const std::string&             postfix,
6830                                  SMESH_Mesh*                    targetMesh,
6831                                  const bool                     topPresent)
6832 {
6833   PGroupIDs newGroupIDs( new list<int> );
6834   SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6835
6836   // Sort existing groups by types and collect their names
6837
6838   // containers to store an old group and generated new ones;
6839   // 1st new group is for result elems of different type than a source one;
6840   // 2nd new group is for same type result elems ("top" group at extrusion)
6841   using boost::tuple;
6842   using boost::make_tuple;
6843   typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
6844   vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
6845   vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
6846   // group names
6847   set< string > groupNames;
6848
6849   SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
6850   if ( !groupIt->more() ) return newGroupIDs;
6851
6852   int newGroupID = mesh->GetGroupIds().back()+1;
6853   while ( groupIt->more() )
6854   {
6855     SMESH_Group * group = groupIt->next();
6856     if ( !group ) continue;
6857     SMESHDS_GroupBase* groupDS = group->GetGroupDS();
6858     if ( !groupDS || groupDS->IsEmpty() ) continue;
6859     groupNames.insert    ( group->GetName() );
6860     groupDS->SetStoreName( group->GetName() );
6861     const SMDSAbs_ElementType type = groupDS->GetType();
6862     SMESHDS_Group* newGroup    = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6863     SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6864     groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
6865     orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
6866   }
6867
6868   // Loop on nodes and elements to add them in new groups
6869
6870   vector< const SMDS_MeshElement* > resultElems;
6871   for ( int isNodes = 0; isNodes < 2; ++isNodes )
6872   {
6873     const SMESH_SequenceOfElemPtr& gens  = isNodes ? nodeGens : elemGens;
6874     const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
6875     if ( gens.size() != elems.size() )
6876       throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
6877
6878     // loop on created elements
6879     for (size_t iElem = 0; iElem < elems.size(); ++iElem )
6880     {
6881       const SMDS_MeshElement* sourceElem = gens[ iElem ];
6882       if ( !sourceElem ) {
6883         MESSAGE("generateGroups(): NULL source element");
6884         continue;
6885       }
6886       list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
6887       if ( groupsOldNew.empty() ) { // no groups of this type at all
6888         while ( iElem+1 < gens.size() && gens[ iElem+1 ] == sourceElem )
6889           ++iElem; // skip all elements made by sourceElem
6890         continue;
6891       }
6892       // collect all elements made by the iElem-th sourceElem
6893       resultElems.clear();
6894       if ( const SMDS_MeshElement* resElem = elems[ iElem ])
6895         if ( resElem != sourceElem )
6896           resultElems.push_back( resElem );
6897       while ( iElem+1 < gens.size() && gens[ iElem+1 ] == sourceElem )
6898         if ( const SMDS_MeshElement* resElem = elems[ ++iElem ])
6899           if ( resElem != sourceElem )
6900             resultElems.push_back( resElem );
6901
6902       const SMDS_MeshElement* topElem = 0;
6903       if ( isNodes ) // there must be a top element
6904       {
6905         topElem = resultElems.back();
6906         resultElems.pop_back();
6907       }
6908       else
6909       {
6910         vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
6911         for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
6912           if ( (*resElemIt)->GetType() == sourceElem->GetType() )
6913           {
6914             topElem = *resElemIt;
6915             *resElemIt = 0; // erase *resElemIt
6916             break;
6917           }
6918       }
6919       // add resultElems to groups originted from ones the sourceElem belongs to
6920       list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
6921       for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
6922       {
6923         SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
6924         if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
6925         {
6926           // fill in a new group
6927           SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
6928           vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
6929           for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
6930             if ( *resElemIt )
6931               newGroup.Add( *resElemIt );
6932
6933           // fill a "top" group
6934           if ( topElem )
6935           {
6936             SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
6937             newTopGroup.Add( topElem );
6938           }
6939         }
6940       }
6941     } // loop on created elements
6942   }// loop on nodes and elements
6943
6944   // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
6945
6946   list<int> topGrouIds;
6947   for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
6948   {
6949     SMESHDS_GroupBase* oldGroupDS =   orderedOldNewGroups[i]->get<0>();
6950     SMESHDS_Group*   newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
6951                                       orderedOldNewGroups[i]->get<2>() };
6952     for ( int is2nd = 0; is2nd < 2; ++is2nd )
6953     {
6954       SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
6955       if ( newGroupDS->IsEmpty() )
6956       {
6957         mesh->GetMeshDS()->RemoveGroup( newGroupDS );
6958       }
6959       else
6960       {
6961         // set group type
6962         newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
6963
6964         // make a name
6965         const bool isTop = ( topPresent &&
6966                              newGroupDS->GetType() == oldGroupDS->GetType() &&
6967                              is2nd );
6968
6969         string name = oldGroupDS->GetStoreName();
6970         { // remove trailing whitespaces (issue 22599)
6971           size_t size = name.size();
6972           while ( size > 1 && isspace( name[ size-1 ]))
6973             --size;
6974           if ( size != name.size() )
6975           {
6976             name.resize( size );
6977             oldGroupDS->SetStoreName( name.c_str() );
6978           }
6979         }
6980         if ( !targetMesh ) {
6981           string suffix = ( isTop ? "top": postfix.c_str() );
6982           name += "_";
6983           name += suffix;
6984           int nb = 1;
6985           while ( !groupNames.insert( name ).second ) // name exists
6986             name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
6987         }
6988         else if ( isTop ) {
6989           name += "_top";
6990         }
6991         newGroupDS->SetStoreName( name.c_str() );
6992
6993         // make a SMESH_Groups
6994         mesh->AddGroup( newGroupDS );
6995         if ( isTop )
6996           topGrouIds.push_back( newGroupDS->GetID() );
6997         else
6998           newGroupIDs->push_back( newGroupDS->GetID() );
6999       }
7000     }
7001   }
7002   newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
7003
7004   return newGroupIDs;
7005 }
7006
7007 //================================================================================
7008 /*!
7009  *  * \brief Return list of group of nodes close to each other within theTolerance
7010  *  *        Search among theNodes or in the whole mesh if theNodes is empty using
7011  *  *        an Octree algorithm
7012  *  \param [in,out] theNodes - the nodes to treat
7013  *  \param [in]     theTolerance - the tolerance
7014  *  \param [out]    theGroupsOfNodes - the result groups of coincident nodes
7015  *  \param [in]     theSeparateCornersAndMedium - if \c true, in quadratic mesh puts 
7016  *         corner and medium nodes in separate groups
7017  */
7018 //================================================================================
7019
7020 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet &   theNodes,
7021                                             const double         theTolerance,
7022                                             TListOfListOfNodes & theGroupsOfNodes,
7023                                             bool                 theSeparateCornersAndMedium)
7024 {
7025   ClearLastCreated();
7026
7027   if ( myMesh->NbEdges  ( ORDER_QUADRATIC ) +
7028        myMesh->NbFaces  ( ORDER_QUADRATIC ) +
7029        myMesh->NbVolumes( ORDER_QUADRATIC ) == 0 )
7030     theSeparateCornersAndMedium = false;
7031
7032   TIDSortedNodeSet& corners = theNodes;
7033   TIDSortedNodeSet  medium;
7034
7035   if ( theNodes.empty() ) // get all nodes in the mesh
7036   {
7037     TIDSortedNodeSet* nodes[2] = { &corners, &medium };
7038     SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(/*idInceasingOrder=*/true);
7039     if ( theSeparateCornersAndMedium )
7040       while ( nIt->more() )
7041       {
7042         const SMDS_MeshNode* n = nIt->next();
7043         TIDSortedNodeSet* & nodeSet = nodes[ SMESH_MesherHelper::IsMedium( n )];
7044         nodeSet->insert( nodeSet->end(), n );
7045       }
7046     else
7047       while ( nIt->more() )
7048         theNodes.insert( theNodes.end(), nIt->next() );
7049   }
7050   else if ( theSeparateCornersAndMedium ) // separate corners from medium nodes
7051   {
7052     TIDSortedNodeSet::iterator nIt = corners.begin();
7053     while ( nIt != corners.end() )
7054       if ( SMESH_MesherHelper::IsMedium( *nIt ))
7055       {
7056         medium.insert( medium.end(), *nIt );
7057         corners.erase( nIt++ );
7058       }
7059       else
7060       {
7061         ++nIt;
7062       }
7063   }
7064
7065   if ( !corners.empty() )
7066     SMESH_OctreeNode::FindCoincidentNodes ( corners, &theGroupsOfNodes, theTolerance );
7067   if ( !medium.empty() )
7068     SMESH_OctreeNode::FindCoincidentNodes ( medium, &theGroupsOfNodes, theTolerance );
7069 }
7070
7071 //=======================================================================
7072 //function : SimplifyFace
7073 //purpose  : split a chain of nodes into several closed chains
7074 //=======================================================================
7075
7076 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
7077                                     vector<const SMDS_MeshNode *>&       poly_nodes,
7078                                     vector<int>&                         quantities) const
7079 {
7080   int nbNodes = faceNodes.size();
7081   while ( faceNodes[ 0 ] == faceNodes[ nbNodes-1 ] && nbNodes > 2 )
7082     --nbNodes;
7083   if ( nbNodes < 3 )
7084     return 0;
7085   size_t prevNbQuant = quantities.size();
7086
7087   vector< const SMDS_MeshNode* > simpleNodes; simpleNodes.reserve( nbNodes );
7088   map< const SMDS_MeshNode*, int > nodeIndices; // indices within simpleNodes
7089   map< const SMDS_MeshNode*, int >::iterator nInd;
7090
7091   nodeIndices.insert( make_pair( faceNodes[0], 0 ));
7092   simpleNodes.push_back( faceNodes[0] );
7093   for ( int iCur = 1; iCur < nbNodes; iCur++ )
7094   {
7095     if ( faceNodes[ iCur ] != simpleNodes.back() )
7096     {
7097       int index = simpleNodes.size();
7098       nInd = nodeIndices.insert( make_pair( faceNodes[ iCur ], index )).first;
7099       int prevIndex = nInd->second;
7100       if ( prevIndex < index )
7101       {
7102         // a sub-loop found
7103         int loopLen = index - prevIndex;
7104         if ( loopLen > 2 )
7105         {
7106           // store the sub-loop
7107           quantities.push_back( loopLen );
7108           for ( int i = prevIndex; i < index; i++ )
7109             poly_nodes.push_back( simpleNodes[ i ]);
7110         }
7111         simpleNodes.resize( prevIndex+1 );
7112       }
7113       else
7114       {
7115         simpleNodes.push_back( faceNodes[ iCur ]);
7116       }
7117     }
7118   }
7119
7120   if ( simpleNodes.size() > 2 )
7121   {
7122     quantities.push_back( simpleNodes.size() );
7123     poly_nodes.insert ( poly_nodes.end(), simpleNodes.begin(), simpleNodes.end() );
7124   }
7125
7126   return quantities.size() - prevNbQuant;
7127 }
7128
7129 //=======================================================================
7130 //function : MergeNodes
7131 //purpose  : In each group, the cdr of nodes are substituted by the first one
7132 //           in all elements.
7133 //=======================================================================
7134
7135 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes,
7136                                    const bool           theAvoidMakingHoles)
7137 {
7138   ClearLastCreated();
7139
7140   SMESHDS_Mesh* mesh = GetMeshDS();
7141
7142   TNodeNodeMap nodeNodeMap; // node to replace - new node
7143   set<const SMDS_MeshElement*> elems; // all elements with changed nodes
7144   list< int > rmElemIds, rmNodeIds;
7145   vector< ElemFeatures > newElemDefs;
7146
7147   // Fill nodeNodeMap and elems
7148
7149   TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
7150   for ( ; grIt != theGroupsOfNodes.end(); grIt++ )
7151   {
7152     list<const SMDS_MeshNode*>& nodes = *grIt;
7153     list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
7154     const SMDS_MeshNode* nToKeep = *nIt;
7155     for ( ++nIt; nIt != nodes.end(); nIt++ )
7156     {
7157       const SMDS_MeshNode* nToRemove = *nIt;
7158       nodeNodeMap.insert( make_pair( nToRemove, nToKeep ));
7159       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
7160       while ( invElemIt->more() ) {
7161         const SMDS_MeshElement* elem = invElemIt->next();
7162         elems.insert(elem);
7163       }
7164     }
7165   }
7166
7167   // Apply recursive replacements (BUG 0020185)
7168   TNodeNodeMap::iterator nnIt = nodeNodeMap.begin();
7169   for ( ; nnIt != nodeNodeMap.end(); ++nnIt )
7170   {
7171     const SMDS_MeshNode* nToKeep = nnIt->second;
7172     TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( nToKeep );
7173     while ( nnIt_i != nodeNodeMap.end() && nnIt_i->second != nnIt->second )
7174       nToKeep = nnIt_i->second;
7175     nnIt->second = nToKeep;
7176   }
7177
7178   if ( theAvoidMakingHoles )
7179   {
7180     // find elements whose topology changes
7181
7182     vector<const SMDS_MeshElement*> pbElems;
7183     set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7184     for ( ; eIt != elems.end(); ++eIt )
7185     {
7186       const SMDS_MeshElement* elem = *eIt;
7187       SMDS_ElemIteratorPtr     itN = elem->nodesIterator();
7188       while ( itN->more() )
7189       {
7190         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7191         TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7192         if ( nnIt != nodeNodeMap.end() && elem->GetNodeIndex( nnIt->second ) >= 0 )
7193         {
7194           // several nodes of elem stick
7195           pbElems.push_back( elem );
7196           break;
7197         }
7198       }
7199     }
7200     // exclude from merge nodes causing spoiling element
7201     for ( size_t iLoop = 0; iLoop < pbElems.size(); ++iLoop ) // avoid infinite cycle
7202     {
7203       bool nodesExcluded = false;
7204       for ( size_t i = 0; i < pbElems.size(); ++i )
7205       {
7206         size_t prevNbMergeNodes = nodeNodeMap.size();
7207         if ( !applyMerge( pbElems[i], newElemDefs, nodeNodeMap, /*noHoles=*/true ) &&
7208              prevNbMergeNodes < nodeNodeMap.size() )
7209           nodesExcluded = true;
7210       }
7211       if ( !nodesExcluded )
7212         break;
7213     }
7214   }
7215
7216   for ( nnIt = nodeNodeMap.begin(); nnIt != nodeNodeMap.end(); ++nnIt )
7217   {
7218     const SMDS_MeshNode* nToRemove = nnIt->first;
7219     const SMDS_MeshNode* nToKeep   = nnIt->second;
7220     if ( nToRemove != nToKeep )
7221     {
7222       rmNodeIds.push_back( nToRemove->GetID() );
7223       AddToSameGroups( nToKeep, nToRemove, mesh );
7224       // set _alwaysComputed to a sub-mesh of VERTEX to enable further mesh computing
7225       // w/o creating node in place of merged ones.
7226       const SMDS_PositionPtr& pos = nToRemove->GetPosition();
7227       if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
7228         if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
7229           sm->SetIsAlwaysComputed( true );
7230     }
7231   }
7232
7233   // Change element nodes or remove an element
7234
7235   set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7236   for ( ; eIt != elems.end(); eIt++ )
7237   {
7238     const SMDS_MeshElement* elem = *eIt;
7239     SMESHDS_SubMesh*          sm = mesh->MeshElements( elem->getshapeId() );
7240
7241     bool keepElem = applyMerge( elem, newElemDefs, nodeNodeMap, /*noHoles=*/false );
7242     if ( !keepElem )
7243       rmElemIds.push_back( elem->GetID() );
7244
7245     for ( size_t i = 0; i < newElemDefs.size(); ++i )
7246     {
7247       if ( i > 0 || !mesh->ChangeElementNodes( elem,
7248                                                & newElemDefs[i].myNodes[0],
7249                                                newElemDefs[i].myNodes.size() ))
7250       {
7251         if ( i == 0 )
7252         {
7253           newElemDefs[i].SetID( elem->GetID() );
7254           mesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
7255           if ( !keepElem ) rmElemIds.pop_back();
7256         }
7257         else
7258         {
7259           newElemDefs[i].SetID( -1 );
7260         }
7261         SMDS_MeshElement* newElem = this->AddElement( newElemDefs[i].myNodes, newElemDefs[i] );
7262         if ( sm && newElem )
7263           sm->AddElement( newElem );
7264         if ( elem != newElem )
7265           ReplaceElemInGroups( elem, newElem, mesh );
7266       }
7267     }
7268   }
7269
7270   // Remove bad elements, then equal nodes (order important)
7271   Remove( rmElemIds, /*isNodes=*/false );
7272   Remove( rmNodeIds, /*isNodes=*/true );
7273
7274   return;
7275 }
7276
7277 //=======================================================================
7278 //function : applyMerge
7279 //purpose  : Compute new connectivity of an element after merging nodes
7280 //  \param [in] elems - the element
7281 //  \param [out] newElemDefs - definition(s) of result element(s)
7282 //  \param [inout] nodeNodeMap - nodes to merge
7283 //  \param [in] avoidMakingHoles - if true and and the element becomes invalid
7284 //              after merging (but not degenerated), removes nodes causing
7285 //              the invalidity from \a nodeNodeMap.
7286 //  \return bool - true if the element should be removed
7287 //=======================================================================
7288
7289 bool SMESH_MeshEditor::applyMerge( const SMDS_MeshElement* elem,
7290                                    vector< ElemFeatures >& newElemDefs,
7291                                    TNodeNodeMap&           nodeNodeMap,
7292                                    const bool              avoidMakingHoles )
7293 {
7294   bool toRemove = false; // to remove elem
7295   int nbResElems = 1;    // nb new elements
7296
7297   newElemDefs.resize(nbResElems);
7298   newElemDefs[0].Init( elem );
7299   newElemDefs[0].myNodes.clear();
7300
7301   set<const SMDS_MeshNode*> nodeSet;
7302   vector< const SMDS_MeshNode*>   curNodes;
7303   vector< const SMDS_MeshNode*> & uniqueNodes = newElemDefs[0].myNodes;
7304   vector<int> iRepl;
7305
7306   const        int  nbNodes = elem->NbNodes();
7307   SMDSAbs_EntityType entity = elem->GetEntityType();
7308
7309   curNodes.resize( nbNodes );
7310   uniqueNodes.resize( nbNodes );
7311   iRepl.resize( nbNodes );
7312   int iUnique = 0, iCur = 0, nbRepl = 0;
7313
7314   // Get new seq of nodes
7315
7316   SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7317   while ( itN->more() )
7318   {
7319     const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7320
7321     TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7322     if ( nnIt != nodeNodeMap.end() ) {
7323       n = (*nnIt).second;
7324     }
7325     curNodes[ iCur ] = n;
7326     bool isUnique = nodeSet.insert( n ).second;
7327     if ( isUnique )
7328       uniqueNodes[ iUnique++ ] = n;
7329     else
7330       iRepl[ nbRepl++ ] = iCur;
7331     iCur++;
7332   }
7333
7334   // Analyse element topology after replacement
7335
7336   int nbUniqueNodes = nodeSet.size();
7337   if ( nbNodes != nbUniqueNodes ) // some nodes stick
7338   {
7339     toRemove = true;
7340     nbResElems = 0;
7341
7342     if ( newElemDefs[0].myIsQuad && newElemDefs[0].myType == SMDSAbs_Face && nbNodes > 6 )
7343     {
7344       // if corner nodes stick, remove medium nodes between them from uniqueNodes
7345       int nbCorners = nbNodes / 2;
7346       for ( int iCur = 0; iCur < nbCorners; ++iCur )
7347       {
7348         int iNext = ( iCur + 1 ) % nbCorners;
7349         if ( curNodes[ iCur ] == curNodes[ iNext ] ) // corners stick
7350         {
7351           int iMedium = iCur + nbCorners;
7352           vector< const SMDS_MeshNode* >::iterator i =
7353             std::find( uniqueNodes.begin() + nbCorners - nbRepl,
7354                        uniqueNodes.end(),
7355                        curNodes[ iMedium ]);
7356           if ( i != uniqueNodes.end() )
7357           {
7358             --nbUniqueNodes;
7359             for ( ; i+1 != uniqueNodes.end(); ++i )
7360               *i = *(i+1);
7361           }
7362         }
7363       }
7364     }
7365
7366     switch ( entity )
7367     {
7368     case SMDSEntity_Polygon:
7369     case SMDSEntity_Quad_Polygon: // Polygon
7370     {
7371       ElemFeatures* elemType = & newElemDefs[0];
7372       const bool isQuad = elemType->myIsQuad;
7373       if ( isQuad )
7374         SMDS_MeshCell::applyInterlace // interlace medium and corner nodes
7375           ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon, nbNodes ), curNodes );
7376
7377       // a polygon can divide into several elements
7378       vector<const SMDS_MeshNode *> polygons_nodes;
7379       vector<int> quantities;
7380       nbResElems = SimplifyFace( curNodes, polygons_nodes, quantities );
7381       newElemDefs.resize( nbResElems );
7382       for ( int inode = 0, iface = 0; iface < nbResElems; iface++ )
7383       {
7384         ElemFeatures* elemType = & newElemDefs[iface];
7385         if ( iface ) elemType->Init( elem );
7386
7387         vector<const SMDS_MeshNode *>& face_nodes = elemType->myNodes;
7388         int nbNewNodes = quantities[iface];
7389         face_nodes.assign( polygons_nodes.begin() + inode,
7390                            polygons_nodes.begin() + inode + nbNewNodes );
7391         inode += nbNewNodes;
7392         if ( isQuad ) // check if a result elem is a valid quadratic polygon
7393         {
7394           bool isValid = ( nbNewNodes % 2 == 0 );
7395           for ( int i = 0; i < nbNewNodes && isValid; ++i )
7396             isValid = ( elem->IsMediumNode( face_nodes[i]) == bool( i % 2 ));
7397           elemType->SetQuad( isValid );
7398           if ( isValid ) // put medium nodes after corners
7399             SMDS_MeshCell::applyInterlaceRev
7400               ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon,
7401                                                     nbNewNodes ), face_nodes );
7402         }
7403         elemType->SetPoly(( nbNewNodes / ( elemType->myIsQuad + 1 ) > 4 ));
7404       }
7405       nbUniqueNodes = newElemDefs[0].myNodes.size();
7406       break;
7407     } // Polygon
7408
7409     case SMDSEntity_Polyhedra: // Polyhedral volume
7410     {
7411       if ( nbUniqueNodes >= 4 )
7412       {
7413         // each face has to be analyzed in order to check volume validity
7414         if ( const SMDS_VtkVolume* aPolyedre = dynamic_cast<const SMDS_VtkVolume*>( elem ))
7415         {
7416           int nbFaces = aPolyedre->NbFaces();
7417
7418           vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7419           vector<int>                  & quantities = newElemDefs[0].myPolyhedQuantities;
7420           vector<const SMDS_MeshNode *>  faceNodes;
7421           poly_nodes.clear();
7422           quantities.clear();
7423
7424           for (int iface = 1; iface <= nbFaces; iface++)
7425           {
7426             int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7427             faceNodes.resize( nbFaceNodes );
7428             for (int inode = 1; inode <= nbFaceNodes; inode++)
7429             {
7430               const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7431               TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7432               if ( nnIt != nodeNodeMap.end() ) // faceNode sticks
7433                 faceNode = (*nnIt).second;
7434               faceNodes[inode - 1] = faceNode;
7435             }
7436             SimplifyFace(faceNodes, poly_nodes, quantities);
7437           }
7438
7439           if ( quantities.size() > 3 )
7440           {
7441             // TODO: remove coincident faces
7442             nbResElems = 1;
7443             nbUniqueNodes = newElemDefs[0].myNodes.size();
7444           }
7445         }
7446       }
7447     }
7448     break;
7449
7450     // Regular elements
7451     // TODO not all the possible cases are solved. Find something more generic?
7452     case SMDSEntity_Edge: //////// EDGE
7453     case SMDSEntity_Triangle: //// TRIANGLE
7454     case SMDSEntity_Quad_Triangle:
7455     case SMDSEntity_Tetra:
7456     case SMDSEntity_Quad_Tetra: // TETRAHEDRON
7457     {
7458       break;
7459     }
7460     case SMDSEntity_Quad_Edge:
7461     {
7462       break;
7463     }
7464     case SMDSEntity_Quadrangle: //////////////////////////////////// QUADRANGLE
7465     {
7466       if ( nbUniqueNodes < 3 )
7467         toRemove = true;
7468       else if ( nbRepl == 1 && curNodes[ iRepl[0]] == curNodes[( iRepl[0]+2 )%4 ])
7469         toRemove = true; // opposite nodes stick
7470       else
7471         toRemove = false;
7472       break;
7473     }
7474     case SMDSEntity_Quad_Quadrangle: // Quadratic QUADRANGLE
7475     {
7476       //   1    5    2
7477       //    +---+---+
7478       //    |       |
7479       //   4+       +6
7480       //    |       |
7481       //    +---+---+
7482       //   0    7    3
7483       if ( nbUniqueNodes == 6 &&
7484            iRepl[0] < 4       &&
7485            ( nbRepl == 1 || iRepl[1] >= 4 ))
7486       {
7487         toRemove = false;
7488       }
7489       break;
7490     }
7491     case SMDSEntity_BiQuad_Quadrangle: // Bi-Quadratic QUADRANGLE
7492     {
7493       //   1    5    2
7494       //    +---+---+
7495       //    |       |
7496       //   4+  8+   +6
7497       //    |       |
7498       //    +---+---+
7499       //   0    7    3
7500       if ( nbUniqueNodes == 7 &&
7501            iRepl[0] < 4       &&
7502            ( nbRepl == 1 || iRepl[1] != 8 ))
7503       {
7504         toRemove = false;
7505       }
7506       break;
7507     }
7508     case SMDSEntity_Penta: ///////////////////////////////////// PENTAHEDRON
7509     {
7510       if ( nbUniqueNodes == 4 ) {
7511         // ---------------------------------> tetrahedron
7512         if ( curNodes[3] == curNodes[4] &&
7513              curNodes[3] == curNodes[5] ) {
7514           // top nodes stick
7515           toRemove = false;
7516         }
7517         else if ( curNodes[0] == curNodes[1] &&
7518                   curNodes[0] == curNodes[2] ) {
7519           // bottom nodes stick: set a top before
7520           uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7521           uniqueNodes[ 0 ] = curNodes [ 5 ];
7522           uniqueNodes[ 1 ] = curNodes [ 4 ];
7523           uniqueNodes[ 2 ] = curNodes [ 3 ];
7524           toRemove = false;
7525         }
7526         else if (( curNodes[0] == curNodes[3] ) +
7527                  ( curNodes[1] == curNodes[4] ) +
7528                  ( curNodes[2] == curNodes[5] ) == 2 ) {
7529           // a lateral face turns into a line
7530           toRemove = false;
7531         }
7532       }
7533       else if ( nbUniqueNodes == 5 ) {
7534         // PENTAHEDRON --------------------> pyramid
7535         if ( curNodes[0] == curNodes[3] )
7536         {
7537           uniqueNodes[ 0 ] = curNodes[ 1 ];
7538           uniqueNodes[ 1 ] = curNodes[ 4 ];
7539           uniqueNodes[ 2 ] = curNodes[ 5 ];
7540           uniqueNodes[ 3 ] = curNodes[ 2 ];
7541           uniqueNodes[ 4 ] = curNodes[ 0 ];
7542           toRemove = false;
7543         }
7544         if ( curNodes[1] == curNodes[4] )
7545         {
7546           uniqueNodes[ 0 ] = curNodes[ 0 ];
7547           uniqueNodes[ 1 ] = curNodes[ 2 ];
7548           uniqueNodes[ 2 ] = curNodes[ 5 ];
7549           uniqueNodes[ 3 ] = curNodes[ 3 ];
7550           uniqueNodes[ 4 ] = curNodes[ 1 ];
7551           toRemove = false;
7552         }
7553         if ( curNodes[2] == curNodes[5] )
7554         {
7555           uniqueNodes[ 0 ] = curNodes[ 0 ];
7556           uniqueNodes[ 1 ] = curNodes[ 3 ];
7557           uniqueNodes[ 2 ] = curNodes[ 4 ];
7558           uniqueNodes[ 3 ] = curNodes[ 1 ];
7559           uniqueNodes[ 4 ] = curNodes[ 2 ];
7560           toRemove = false;
7561         }
7562       }
7563       break;
7564     }
7565     case SMDSEntity_Hexa:
7566     {
7567       //////////////////////////////////// HEXAHEDRON
7568       SMDS_VolumeTool hexa (elem);
7569       hexa.SetExternalNormal();
7570       if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7571         //////////////////////// HEX ---> tetrahedron
7572         for ( int iFace = 0; iFace < 6; iFace++ ) {
7573           const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7574           if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7575               curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7576               curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7577             // one face turns into a point ...
7578             int  pickInd = ind[ 0 ];
7579             int iOppFace = hexa.GetOppFaceIndex( iFace );
7580             ind = hexa.GetFaceNodesIndices( iOppFace );
7581             int nbStick = 0;
7582             uniqueNodes.clear();
7583             for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7584               if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7585                 nbStick++;
7586               else
7587                 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7588             }
7589             if ( nbStick == 1 ) {
7590               // ... and the opposite one - into a triangle.
7591               // set a top node
7592               uniqueNodes.push_back( curNodes[ pickInd ]);
7593               toRemove = false;
7594             }
7595             break;
7596           }
7597         }
7598       }
7599       else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7600         //////////////////////// HEX ---> prism
7601         int nbTria = 0, iTria[3];
7602         const int *ind; // indices of face nodes
7603         // look for triangular faces
7604         for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7605           ind = hexa.GetFaceNodesIndices( iFace );
7606           TIDSortedNodeSet faceNodes;
7607           for ( iCur = 0; iCur < 4; iCur++ )
7608             faceNodes.insert( curNodes[ind[iCur]] );
7609           if ( faceNodes.size() == 3 )
7610             iTria[ nbTria++ ] = iFace;
7611         }
7612         // check if triangles are opposite
7613         if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7614         {
7615           // set nodes of the bottom triangle
7616           ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7617           vector<int> indB;
7618           for ( iCur = 0; iCur < 4; iCur++ )
7619             if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7620               indB.push_back( ind[iCur] );
7621           if ( !hexa.IsForward() )
7622             std::swap( indB[0], indB[2] );
7623           for ( iCur = 0; iCur < 3; iCur++ )
7624             uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7625           // set nodes of the top triangle
7626           const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7627           for ( iCur = 0; iCur < 3; ++iCur )
7628             for ( int j = 0; j < 4; ++j )
7629               if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7630               {
7631                 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7632                 break;
7633               }
7634           toRemove = false;
7635           break;
7636         }
7637       }
7638       else if (nbUniqueNodes == 5 && nbRepl == 3 ) {
7639         //////////////////// HEXAHEDRON ---> pyramid
7640         for ( int iFace = 0; iFace < 6; iFace++ ) {
7641           const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7642           if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7643               curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7644               curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7645             // one face turns into a point ...
7646             int iOppFace = hexa.GetOppFaceIndex( iFace );
7647             ind = hexa.GetFaceNodesIndices( iOppFace );
7648             uniqueNodes.clear();
7649             for ( iCur = 0; iCur < 4; iCur++ ) {
7650               if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7651                 break;
7652               else
7653                 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7654             }
7655             if ( uniqueNodes.size() == 4 ) {
7656               // ... and the opposite one is a quadrangle
7657               // set a top node
7658               const int* indTop = hexa.GetFaceNodesIndices( iFace );
7659               uniqueNodes.push_back( curNodes[indTop[ 0 ]]);
7660               toRemove = false;
7661             }
7662             break;
7663           }
7664         }
7665       }
7666
7667       if ( toRemove && nbUniqueNodes > 4 ) {
7668         ////////////////// HEXAHEDRON ---> polyhedron
7669         hexa.SetExternalNormal();
7670         vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7671         vector<int>                  & quantities = newElemDefs[0].myPolyhedQuantities;
7672         poly_nodes.reserve( 6 * 4 ); poly_nodes.clear();
7673         quantities.reserve( 6 );     quantities.clear();
7674         for ( int iFace = 0; iFace < 6; iFace++ )
7675         {
7676           const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7677           if ( curNodes[ind[0]] == curNodes[ind[2]] ||
7678                curNodes[ind[1]] == curNodes[ind[3]] )
7679           {
7680             quantities.clear();
7681             break; // opposite nodes stick
7682           }
7683           nodeSet.clear();
7684           for ( iCur = 0; iCur < 4; iCur++ )
7685           {
7686             if ( nodeSet.insert( curNodes[ind[ iCur ]] ).second )
7687               poly_nodes.push_back( curNodes[ind[ iCur ]]);
7688           }
7689           if ( nodeSet.size() < 3 )
7690             poly_nodes.resize( poly_nodes.size() - nodeSet.size() );
7691           else
7692             quantities.push_back( nodeSet.size() );
7693         }
7694         if ( quantities.size() >= 4 )
7695         {
7696           nbResElems = 1;
7697           nbUniqueNodes = poly_nodes.size();
7698           newElemDefs[0].SetPoly(true);
7699         }
7700       }
7701       break;
7702     } // case HEXAHEDRON
7703
7704     default:
7705       toRemove = true;
7706
7707     } // switch ( entity )
7708
7709     if ( toRemove && nbResElems == 0 && avoidMakingHoles )
7710     {
7711       // erase from nodeNodeMap nodes whose merge spoils elem
7712       vector< const SMDS_MeshNode* > noMergeNodes;
7713       SMESH_MeshAlgos::DeMerge( elem, curNodes, noMergeNodes );
7714       for ( size_t i = 0; i < noMergeNodes.size(); ++i )
7715         nodeNodeMap.erase( noMergeNodes[i] );
7716     }
7717     
7718   } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7719
7720   uniqueNodes.resize( nbUniqueNodes );
7721
7722   if ( !toRemove && nbResElems == 0 )
7723     nbResElems = 1;
7724
7725   newElemDefs.resize( nbResElems );
7726
7727   return !toRemove;
7728 }
7729
7730
7731 // ========================================================
7732 // class   : ComparableElement
7733 // purpose : allow comparing elements basing on their nodes
7734 // ========================================================
7735
7736 class ComparableElement : public boost::container::flat_set< int >
7737 {
7738   typedef boost::container::flat_set< int >  int_set;
7739
7740   const SMDS_MeshElement* myElem;
7741   int                     mySumID;
7742   mutable int             myGroupID;
7743
7744 public:
7745
7746   ComparableElement( const SMDS_MeshElement* theElem ):
7747     myElem ( theElem ), mySumID( 0 ), myGroupID( -1 )
7748   {
7749     this->reserve( theElem->NbNodes() );
7750     for ( SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator(); nodeIt->more(); )
7751     {
7752       int id = nodeIt->next()->GetID();
7753       mySumID += id;
7754       this->insert( id );
7755     }
7756   }
7757
7758   const SMDS_MeshElement* GetElem() const { return myElem; }
7759
7760   int& GroupID() const { return myGroupID; }
7761   //int& GroupID() const { return const_cast< int& >( myGroupID ); }
7762
7763   ComparableElement( const ComparableElement& theSource ) // move copy
7764   {
7765     ComparableElement& src = const_cast< ComparableElement& >( theSource );
7766     (int_set&) (*this ) = boost::move( src );
7767     myElem    = src.myElem;
7768     mySumID   = src.mySumID;
7769     myGroupID = src.myGroupID;
7770   }
7771
7772   static int HashCode(const ComparableElement& se, int limit )
7773   {
7774     return ::HashCode( se.mySumID, limit );
7775   }
7776   static Standard_Boolean IsEqual(const ComparableElement& se1, const ComparableElement& se2 )
7777   {
7778     return ( se1 == se2 );
7779   }
7780
7781 };
7782
7783 //=======================================================================
7784 //function : FindEqualElements
7785 //purpose  : Return list of group of elements built on the same nodes.
7786 //           Search among theElements or in the whole mesh if theElements is empty
7787 //=======================================================================
7788
7789 void SMESH_MeshEditor::FindEqualElements( TIDSortedElemSet &        theElements,
7790                                           TListOfListOfElementsID & theGroupsOfElementsID )
7791 {
7792   ClearLastCreated();
7793
7794   SMDS_ElemIteratorPtr elemIt;
7795   if ( theElements.empty() ) elemIt = GetMeshDS()->elementsIterator();
7796   else                       elemIt = SMESHUtils::elemSetIterator( theElements );
7797
7798   typedef NCollection_Map< ComparableElement, ComparableElement > TMapOfElements;
7799   typedef std::list<int>                                          TGroupOfElems;
7800   TMapOfElements               mapOfElements;
7801   std::vector< TGroupOfElems > arrayOfGroups;
7802   TGroupOfElems                groupOfElems;
7803
7804   while ( elemIt->more() )
7805   {
7806     const SMDS_MeshElement* curElem = elemIt->next();
7807     ComparableElement      compElem = curElem;
7808     // check uniqueness
7809     const ComparableElement& elemInSet = mapOfElements.Added( compElem );
7810     if ( elemInSet.GetElem() != curElem ) // coincident elem
7811     {
7812       int& iG = elemInSet.GroupID();
7813       if ( iG < 0 )
7814       {
7815         iG = arrayOfGroups.size();
7816         arrayOfGroups.push_back( groupOfElems );
7817         arrayOfGroups[ iG ].push_back( elemInSet.GetElem()->GetID() );
7818       }
7819       arrayOfGroups[ iG ].push_back( curElem->GetID() );
7820     }
7821   }
7822
7823   groupOfElems.clear();
7824   std::vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
7825   for ( ; groupIt != arrayOfGroups.end(); ++groupIt )
7826   {
7827     if ( groupIt->size() > 1 ) {
7828       //groupOfElems.sort(); -- theElements are sorted already
7829       theGroupsOfElementsID.emplace_back( *groupIt );
7830     }
7831   }
7832 }
7833
7834 //=======================================================================
7835 //function : MergeElements
7836 //purpose  : In each given group, substitute all elements by the first one.
7837 //=======================================================================
7838
7839 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
7840 {
7841   ClearLastCreated();
7842
7843   typedef list<int> TListOfIDs;
7844   TListOfIDs rmElemIds; // IDs of elems to remove
7845
7846   SMESHDS_Mesh* aMesh = GetMeshDS();
7847
7848   TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
7849   while ( groupsIt != theGroupsOfElementsID.end() ) {
7850     TListOfIDs& aGroupOfElemID = *groupsIt;
7851     aGroupOfElemID.sort();
7852     int elemIDToKeep = aGroupOfElemID.front();
7853     const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
7854     aGroupOfElemID.pop_front();
7855     TListOfIDs::iterator idIt = aGroupOfElemID.begin();
7856     while ( idIt != aGroupOfElemID.end() ) {
7857       int elemIDToRemove = *idIt;
7858       const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
7859       // add the kept element in groups of removed one (PAL15188)
7860       AddToSameGroups( elemToKeep, elemToRemove, aMesh );
7861       rmElemIds.push_back( elemIDToRemove );
7862       ++idIt;
7863     }
7864     ++groupsIt;
7865   }
7866
7867   Remove( rmElemIds, false );
7868 }
7869
7870 //=======================================================================
7871 //function : MergeEqualElements
7872 //purpose  : Remove all but one of elements built on the same nodes.
7873 //=======================================================================
7874
7875 void SMESH_MeshEditor::MergeEqualElements()
7876 {
7877   TIDSortedElemSet aMeshElements; /* empty input ==
7878                                      to merge equal elements in the whole mesh */
7879   TListOfListOfElementsID aGroupsOfElementsID;
7880   FindEqualElements( aMeshElements, aGroupsOfElementsID );
7881   MergeElements( aGroupsOfElementsID );
7882 }
7883
7884 //=======================================================================
7885 //function : findAdjacentFace
7886 //purpose  :
7887 //=======================================================================
7888
7889 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
7890                                                 const SMDS_MeshNode* n2,
7891                                                 const SMDS_MeshElement* elem)
7892 {
7893   TIDSortedElemSet elemSet, avoidSet;
7894   if ( elem )
7895     avoidSet.insert ( elem );
7896   return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
7897 }
7898
7899 //=======================================================================
7900 //function : findSegment
7901 //purpose  : Return a mesh segment by two nodes one of which can be medium
7902 //=======================================================================
7903
7904 static const SMDS_MeshElement* findSegment(const SMDS_MeshNode* n1,
7905                                            const SMDS_MeshNode* n2)
7906 {
7907   SMDS_ElemIteratorPtr it = n1->GetInverseElementIterator( SMDSAbs_Edge );
7908   while ( it->more() )
7909   {
7910     const SMDS_MeshElement* seg = it->next();
7911     if ( seg->GetNodeIndex( n2 ) >= 0 )
7912       return seg;
7913   }
7914   return 0;
7915 }
7916
7917 //=======================================================================
7918 //function : FindFreeBorder
7919 //purpose  :
7920 //=======================================================================
7921
7922 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
7923
7924 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode*             theFirstNode,
7925                                        const SMDS_MeshNode*             theSecondNode,
7926                                        const SMDS_MeshNode*             theLastNode,
7927                                        list< const SMDS_MeshNode* > &   theNodes,
7928                                        list< const SMDS_MeshElement* >& theFaces)
7929 {
7930   if ( !theFirstNode || !theSecondNode )
7931     return false;
7932   // find border face between theFirstNode and theSecondNode
7933   const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
7934   if ( !curElem )
7935     return false;
7936
7937   theFaces.push_back( curElem );
7938   theNodes.push_back( theFirstNode );
7939   theNodes.push_back( theSecondNode );
7940
7941   const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
7942   TIDSortedElemSet foundElems;
7943   bool needTheLast = ( theLastNode != 0 );
7944
7945   while ( nStart != theLastNode ) {
7946     if ( nStart == theFirstNode )
7947       return !needTheLast;
7948
7949     // find all free border faces sharing form nStart
7950
7951     list< const SMDS_MeshElement* > curElemList;
7952     list< const SMDS_MeshNode* >    nStartList;
7953     SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
7954     while ( invElemIt->more() ) {
7955       const SMDS_MeshElement* e = invElemIt->next();
7956       if ( e == curElem || foundElems.insert( e ).second ) {
7957         // get nodes
7958         int iNode = 0, nbNodes = e->NbNodes();
7959         vector<const SMDS_MeshNode*> nodes(nbNodes+1);
7960
7961         if ( e->IsQuadratic() ) {
7962           const SMDS_VtkFace* F =
7963             dynamic_cast<const SMDS_VtkFace*>(e);
7964           if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
7965           // use special nodes iterator
7966           SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
7967           while( anIter->more() ) {
7968             nodes[ iNode++ ] = cast2Node(anIter->next());
7969           }
7970         }
7971         else {
7972           SMDS_ElemIteratorPtr nIt = e->nodesIterator();
7973           while ( nIt->more() )
7974             nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
7975         }
7976         nodes[ iNode ] = nodes[ 0 ];
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           if(elem->IsQuadratic()) {
8236             const SMDS_VtkFace* F =
8237               dynamic_cast<const SMDS_VtkFace*>(elem);
8238             if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8239             // use special nodes iterator
8240             SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8241             while( anIter->more() ) {
8242               nodes[ iNode ] = cast2Node(anIter->next());
8243               if ( nodes[ iNode++ ] == prevSideNode )
8244                 iPrevNode = iNode - 1;
8245             }
8246           }
8247           else {
8248             SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
8249             while ( nIt->more() ) {
8250               nodes[ iNode ] = cast2Node( nIt->next() );
8251               if ( nodes[ iNode++ ] == prevSideNode )
8252                 iPrevNode = iNode - 1;
8253             }
8254           }
8255           // there are 2 links to check
8256           nbNodes = 2;
8257         }
8258         else // --edge
8259           continue;
8260         // loop on links, to be precise, on the second node of links
8261         for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8262           const SMDS_MeshNode* n = nodes[ iNode ];
8263           if ( isVolume ) {
8264             if ( !volume.IsLinked( n, prevSideNode ))
8265               continue;
8266           }
8267           else {
8268             if ( iNode ) // a node before prevSideNode
8269               n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8270             else         // a node after prevSideNode
8271               n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8272           }
8273           // check if this link was already used
8274           long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8275           bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8276           if (!isJustChecked &&
8277               foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8278           {
8279             // test a link geometrically
8280             gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8281             bool linkIsBetter = false;
8282             double dot = 0.0, dist = 0.0;
8283             if ( searchByDir ) { // choose most co-directed link
8284               dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8285               linkIsBetter = ( dot > maxDot );
8286             }
8287             else { // choose link with the node closest to bordPos
8288               dist = ( nextXYZ - bordPos ).SquareModulus();
8289               linkIsBetter = ( dist < minDist );
8290             }
8291             if ( linkIsBetter ) {
8292               maxDot = dot;
8293               minDist = dist;
8294               linkID = iLink;
8295               sideNode = n;
8296               sideElem = elem;
8297             }
8298           }
8299         }
8300       } // loop on inverse elements of prevSideNode
8301
8302       if ( !sideNode ) {
8303         MESSAGE(" Can't find path by links of the Side 2 ");
8304         return SEW_BAD_SIDE_NODES;
8305       }
8306       sideNodes.push_back( sideNode );
8307       sideElems.push_back( sideElem );
8308       foundSideLinkIDs.insert ( linkID );
8309       prevSideNode = sideNode;
8310
8311       if ( *nBordIt == theBordLastNode )
8312         searchByDir = false;
8313       else {
8314         // find the next border link to compare with
8315         gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8316         searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8317         // move to next border node if sideNode is before forward border node (bordPos)
8318         while ( *nBordIt != theBordLastNode && !searchByDir ) {
8319           prevBordNode = *nBordIt;
8320           nBordIt++;
8321           bordPos = nBordXYZ[ *nBordIt ];
8322           bordDir = bordPos - nBordXYZ[ prevBordNode ];
8323           searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8324         }
8325       }
8326     }
8327     while ( sideNode != theSideSecondNode );
8328
8329     if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8330       MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8331       return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8332     }
8333   } // end nodes search on the side 2
8334
8335   // ============================
8336   // sew the border to the side 2
8337   // ============================
8338
8339   int nbNodes[]  = { (int)nSide[0].size(), (int)nSide[1].size() };
8340   int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8341
8342   bool toMergeConformal = ( nbNodes[0] == nbNodes[1] );
8343   if ( toMergeConformal && toCreatePolygons )
8344   {
8345     // do not merge quadrangles if polygons are OK (IPAL0052824)
8346     eIt[0] = eSide[0].begin();
8347     eIt[1] = eSide[1].begin();
8348     bool allQuads[2] = { true, true };
8349     for ( int iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8350       for ( ; allQuads[iBord] && eIt[iBord] != eSide[iBord].end(); ++eIt[iBord] )
8351         allQuads[iBord] = ( (*eIt[iBord])->NbCornerNodes() == 4 );
8352     }
8353     toMergeConformal = ( !allQuads[0] && !allQuads[1] );
8354   }
8355
8356   TListOfListOfNodes nodeGroupsToMerge;
8357   if (( toMergeConformal ) ||
8358       ( theSideIsFreeBorder && !theSideThirdNode )) {
8359
8360     // all nodes are to be merged
8361
8362     for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8363          nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8364          nIt[0]++, nIt[1]++ )
8365     {
8366       nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8367       nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8368       nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8369     }
8370   }
8371   else {
8372
8373     // insert new nodes into the border and the side to get equal nb of segments
8374
8375     // get normalized parameters of nodes on the borders
8376     vector< double > param[ 2 ];
8377     param[0].resize( maxNbNodes );
8378     param[1].resize( maxNbNodes );
8379     int iNode, iBord;
8380     for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8381       list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8382       list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8383       const SMDS_MeshNode* nPrev = *nIt;
8384       double bordLength = 0;
8385       for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8386         const SMDS_MeshNode* nCur = *nIt;
8387         gp_XYZ segment (nCur->X() - nPrev->X(),
8388                         nCur->Y() - nPrev->Y(),
8389                         nCur->Z() - nPrev->Z());
8390         double segmentLen = segment.Modulus();
8391         bordLength += segmentLen;
8392         param[ iBord ][ iNode ] = bordLength;
8393         nPrev = nCur;
8394       }
8395       // normalize within [0,1]
8396       for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8397         param[ iBord ][ iNode ] /= bordLength;
8398       }
8399     }
8400
8401     // loop on border segments
8402     const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8403     int i[ 2 ] = { 0, 0 };
8404     nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8405     nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8406
8407     TElemOfNodeListMap insertMap;
8408     TElemOfNodeListMap::iterator insertMapIt;
8409     // insertMap is
8410     // key:   elem to insert nodes into
8411     // value: 2 nodes to insert between + nodes to be inserted
8412     do {
8413       bool next[ 2 ] = { false, false };
8414
8415       // find min adjacent segment length after sewing
8416       double nextParam = 10., prevParam = 0;
8417       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8418         if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8419           nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8420         if ( i[ iBord ] > 0 )
8421           prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8422       }
8423       double  minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8424       double  maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8425       double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8426
8427       // choose to insert or to merge nodes
8428       double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8429       if ( Abs( du ) <= minSegLen * 0.2 ) {
8430         // merge
8431         // ------
8432         nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8433         const SMDS_MeshNode* n0 = *nIt[0];
8434         const SMDS_MeshNode* n1 = *nIt[1];
8435         nodeGroupsToMerge.back().push_back( n1 );
8436         nodeGroupsToMerge.back().push_back( n0 );
8437         // position of node of the border changes due to merge
8438         param[ 0 ][ i[0] ] += du;
8439         // move n1 for the sake of elem shape evaluation during insertion.
8440         // n1 will be removed by MergeNodes() anyway
8441         const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8442         next[0] = next[1] = true;
8443       }
8444       else {
8445         // insert
8446         // ------
8447         int intoBord = ( du < 0 ) ? 0 : 1;
8448         const SMDS_MeshElement* elem = *eIt [ intoBord ];
8449         const SMDS_MeshNode*    n1   = nPrev[ intoBord ];
8450         const SMDS_MeshNode*    n2   = *nIt [ intoBord ];
8451         const SMDS_MeshNode*    nIns = *nIt [ 1 - intoBord ];
8452         if ( intoBord == 1 ) {
8453           // move node of the border to be on a link of elem of the side
8454           gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8455           gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8456           double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8457           gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8458           GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8459         }
8460         insertMapIt = insertMap.find( elem );
8461         bool  notFound = ( insertMapIt == insertMap.end() );
8462         bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8463         if ( otherLink ) {
8464           // insert into another link of the same element:
8465           // 1. perform insertion into the other link of the elem
8466           list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8467           const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8468           const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8469           InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8470           // 2. perform insertion into the link of adjacent faces
8471           while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem )) {
8472             InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8473           }
8474           while ( const SMDS_MeshElement* seg = findSegment( n12, n22 )) {
8475             InsertNodesIntoLink( seg, n12, n22, nodeList );
8476           }
8477           if (toCreatePolyedrs) {
8478             // perform insertion into the links of adjacent volumes
8479             UpdateVolumes(n12, n22, nodeList);
8480           }
8481           // 3. find an element appeared on n1 and n2 after the insertion
8482           insertMap.erase( elem );
8483           elem = findAdjacentFace( n1, n2, 0 );
8484         }
8485         if ( notFound || otherLink ) {
8486           // add element and nodes of the side into the insertMap
8487           insertMapIt = insertMap.insert( make_pair( elem, list<const SMDS_MeshNode*>() )).first;
8488           (*insertMapIt).second.push_back( n1 );
8489           (*insertMapIt).second.push_back( n2 );
8490         }
8491         // add node to be inserted into elem
8492         (*insertMapIt).second.push_back( nIns );
8493         next[ 1 - intoBord ] = true;
8494       }
8495
8496       // go to the next segment
8497       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8498         if ( next[ iBord ] ) {
8499           if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8500             eIt[ iBord ]++;
8501           nPrev[ iBord ] = *nIt[ iBord ];
8502           nIt[ iBord ]++; i[ iBord ]++;
8503         }
8504       }
8505     }
8506     while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8507
8508     // perform insertion of nodes into elements
8509
8510     for (insertMapIt = insertMap.begin();
8511          insertMapIt != insertMap.end();
8512          insertMapIt++ )
8513     {
8514       const SMDS_MeshElement* elem = (*insertMapIt).first;
8515       list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8516       const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8517       const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8518
8519       InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8520
8521       while ( const SMDS_MeshElement* seg = findSegment( n1, n2 )) {
8522         InsertNodesIntoLink( seg, n1, n2, nodeList );
8523       }
8524
8525       if ( !theSideIsFreeBorder ) {
8526         // look for and insert nodes into the faces adjacent to elem
8527         while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem )) {
8528           InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8529         }
8530       }
8531       if (toCreatePolyedrs) {
8532         // perform insertion into the links of adjacent volumes
8533         UpdateVolumes(n1, n2, nodeList);
8534       }
8535     }
8536   } // end: insert new nodes
8537
8538   MergeNodes ( nodeGroupsToMerge );
8539
8540
8541   // Remove coincident segments
8542
8543   // get new segments
8544   TIDSortedElemSet segments;
8545   SMESH_SequenceOfElemPtr newFaces;
8546   for ( size_t i = 0; i < myLastCreatedElems.size(); ++i )
8547   {
8548     if ( !myLastCreatedElems[i] ) continue;
8549     if ( myLastCreatedElems[i]->GetType() == SMDSAbs_Edge )
8550       segments.insert( segments.end(), myLastCreatedElems[i] );
8551     else
8552       newFaces.push_back( myLastCreatedElems[i] );
8553   }
8554   // get segments adjacent to merged nodes
8555   TListOfListOfNodes::iterator groupIt = nodeGroupsToMerge.begin();
8556   for ( ; groupIt != nodeGroupsToMerge.end(); groupIt++ )
8557   {
8558     const list<const SMDS_MeshNode*>& nodes = *groupIt;
8559     SMDS_ElemIteratorPtr segIt = nodes.front()->GetInverseElementIterator( SMDSAbs_Edge );
8560     while ( segIt->more() )
8561       segments.insert( segIt->next() );
8562   }
8563
8564   // find coincident
8565   TListOfListOfElementsID equalGroups;
8566   if ( !segments.empty() )
8567     FindEqualElements( segments, equalGroups );
8568   if ( !equalGroups.empty() )
8569   {
8570     // remove from segments those that will be removed
8571     TListOfListOfElementsID::iterator itGroups = equalGroups.begin();
8572     for ( ; itGroups != equalGroups.end(); ++itGroups )
8573     {
8574       list< int >& group = *itGroups;
8575       list< int >::iterator id = group.begin();
8576       for ( ++id; id != group.end(); ++id )
8577         if ( const SMDS_MeshElement* seg = GetMeshDS()->FindElement( *id ))
8578           segments.erase( seg );
8579     }
8580     // remove equal segments
8581     MergeElements( equalGroups );
8582
8583     // restore myLastCreatedElems
8584     myLastCreatedElems = newFaces;
8585     TIDSortedElemSet::iterator seg = segments.begin();
8586     for ( ; seg != segments.end(); ++seg )
8587       myLastCreatedElems.push_back( *seg );
8588   }
8589
8590   return aResult;
8591 }
8592
8593 //=======================================================================
8594 //function : InsertNodesIntoLink
8595 //purpose  : insert theNodesToInsert into theElement between theBetweenNode1
8596 //           and theBetweenNode2 and split theElement
8597 //=======================================================================
8598
8599 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement*     theElement,
8600                                            const SMDS_MeshNode*        theBetweenNode1,
8601                                            const SMDS_MeshNode*        theBetweenNode2,
8602                                            list<const SMDS_MeshNode*>& theNodesToInsert,
8603                                            const bool                  toCreatePoly)
8604 {
8605   if ( !theElement ) return;
8606
8607   SMESHDS_Mesh *aMesh = GetMeshDS();
8608   vector<const SMDS_MeshElement*> newElems;
8609
8610   if ( theElement->GetType() == SMDSAbs_Edge )
8611   {
8612     theNodesToInsert.push_front( theBetweenNode1 );
8613     theNodesToInsert.push_back ( theBetweenNode2 );
8614     list<const SMDS_MeshNode*>::iterator n = theNodesToInsert.begin();
8615     const SMDS_MeshNode* n1 = *n;
8616     for ( ++n; n != theNodesToInsert.end(); ++n )
8617     {
8618       const SMDS_MeshNode* n2 = *n;
8619       if ( const SMDS_MeshElement* seg = aMesh->FindEdge( n1, n2 ))
8620         AddToSameGroups( seg, theElement, aMesh );
8621       else
8622         newElems.push_back( aMesh->AddEdge ( n1, n2 ));
8623       n1 = n2;
8624     }
8625     theNodesToInsert.pop_front();
8626     theNodesToInsert.pop_back();
8627
8628     if ( theElement->IsQuadratic() ) // add a not split part
8629     {
8630       vector<const SMDS_MeshNode*> nodes( theElement->begin_nodes(),
8631                                           theElement->end_nodes() );
8632       int iOther = 0, nbN = nodes.size();
8633       for ( ; iOther < nbN; ++iOther )
8634         if ( nodes[iOther] != theBetweenNode1 &&
8635              nodes[iOther] != theBetweenNode2 )
8636           break;
8637       if      ( iOther == 0 )
8638       {
8639         if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[0], nodes[1] ))
8640           AddToSameGroups( seg, theElement, aMesh );
8641         else
8642           newElems.push_back( aMesh->AddEdge ( nodes[0], nodes[1] ));
8643       }
8644       else if ( iOther == 2 )
8645       {
8646         if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[1], nodes[2] ))
8647           AddToSameGroups( seg, theElement, aMesh );
8648         else
8649           newElems.push_back( aMesh->AddEdge ( nodes[1], nodes[2] ));
8650       }
8651     }
8652     // treat new elements
8653     for ( size_t i = 0; i < newElems.size(); ++i )
8654       if ( newElems[i] )
8655       {
8656         aMesh->SetMeshElementOnShape( newElems[i], theElement->getshapeId() );
8657         myLastCreatedElems.push_back( newElems[i] );
8658       }
8659     ReplaceElemInGroups( theElement, newElems, aMesh );
8660     aMesh->RemoveElement( theElement );
8661     return;
8662
8663   } // if ( theElement->GetType() == SMDSAbs_Edge )
8664
8665   const SMDS_MeshElement* theFace = theElement;
8666   if ( theFace->GetType() != SMDSAbs_Face ) return;
8667
8668   // find indices of 2 link nodes and of the rest nodes
8669   int iNode = 0, il1, il2, i3, i4;
8670   il1 = il2 = i3 = i4 = -1;
8671   vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8672
8673   SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8674   while ( nodeIt->more() ) {
8675     const SMDS_MeshNode* n = nodeIt->next();
8676     if ( n == theBetweenNode1 )
8677       il1 = iNode;
8678     else if ( n == theBetweenNode2 )
8679       il2 = iNode;
8680     else if ( i3 < 0 )
8681       i3 = iNode;
8682     else
8683       i4 = iNode;
8684     nodes[ iNode++ ] = n;
8685   }
8686   if ( il1 < 0 || il2 < 0 || i3 < 0 )
8687     return ;
8688
8689   // arrange link nodes to go one after another regarding the face orientation
8690   bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8691   list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8692   if ( reverse ) {
8693     iNode = il1;
8694     il1 = il2;
8695     il2 = iNode;
8696     aNodesToInsert.reverse();
8697   }
8698   // check that not link nodes of a quadrangles are in good order
8699   int nbFaceNodes = theFace->NbNodes();
8700   if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8701     iNode = i3;
8702     i3 = i4;
8703     i4 = iNode;
8704   }
8705
8706   if (toCreatePoly || theFace->IsPoly()) {
8707
8708     iNode = 0;
8709     vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8710
8711     // add nodes of face up to first node of link
8712     bool isFLN = false;
8713
8714     if ( theFace->IsQuadratic() ) {
8715       const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>(theFace);
8716       if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8717       // use special nodes iterator
8718       SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8719       while( anIter->more()  && !isFLN ) {
8720         const SMDS_MeshNode* n = cast2Node(anIter->next());
8721         poly_nodes[iNode++] = n;
8722         if (n == nodes[il1]) {
8723           isFLN = true;
8724         }
8725       }
8726       // add nodes to insert
8727       list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8728       for (; nIt != aNodesToInsert.end(); nIt++) {
8729         poly_nodes[iNode++] = *nIt;
8730       }
8731       // add nodes of face starting from last node of link
8732       while ( anIter->more() ) {
8733         poly_nodes[iNode++] = cast2Node(anIter->next());
8734       }
8735     }
8736     else {
8737       SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8738       while ( nodeIt->more() && !isFLN ) {
8739         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8740         poly_nodes[iNode++] = n;
8741         if (n == nodes[il1]) {
8742           isFLN = true;
8743         }
8744       }
8745       // add nodes to insert
8746       list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8747       for (; nIt != aNodesToInsert.end(); nIt++) {
8748         poly_nodes[iNode++] = *nIt;
8749       }
8750       // add nodes of face starting from last node of link
8751       while ( nodeIt->more() ) {
8752         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8753         poly_nodes[iNode++] = n;
8754       }
8755     }
8756
8757     // make a new face
8758     newElems.push_back( aMesh->AddPolygonalFace( poly_nodes ));
8759   }
8760
8761   else if ( !theFace->IsQuadratic() )
8762   {
8763     // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8764     int nbLinkNodes = 2 + aNodesToInsert.size();
8765     //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8766     vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8767     linkNodes[ 0 ] = nodes[ il1 ];
8768     linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8769     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8770     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8771       linkNodes[ iNode++ ] = *nIt;
8772     }
8773     // decide how to split a quadrangle: compare possible variants
8774     // and choose which of splits to be a quadrangle
8775     int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad = 0;
8776     if ( nbFaceNodes == 3 ) {
8777       iBestQuad = nbSplits;
8778       i4 = i3;
8779     }
8780     else if ( nbFaceNodes == 4 ) {
8781       SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8782       double aBestRate = DBL_MAX;
8783       for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8784         i1 = 0; i2 = 1;
8785         double aBadRate = 0;
8786         // evaluate elements quality
8787         for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8788           if ( iSplit == iQuad ) {
8789             SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8790                                    linkNodes[ i2++ ],
8791                                    nodes[ i3 ],
8792                                    nodes[ i4 ]);
8793             aBadRate += getBadRate( &quad, aCrit );
8794           }
8795           else {
8796             SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8797                                    linkNodes[ i2++ ],
8798                                    nodes[ iSplit < iQuad ? i4 : i3 ]);
8799             aBadRate += getBadRate( &tria, aCrit );
8800           }
8801         }
8802         // choice
8803         if ( aBadRate < aBestRate ) {
8804           iBestQuad = iQuad;
8805           aBestRate = aBadRate;
8806         }
8807       }
8808     }
8809
8810     // create new elements
8811     i1 = 0; i2 = 1;
8812     for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ )
8813     {
8814       if ( iSplit == iBestQuad )
8815         newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8816                                             linkNodes[ i2++ ],
8817                                             nodes[ i3 ],
8818                                             nodes[ i4 ]));
8819       else
8820         newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8821                                             linkNodes[ i2++ ],
8822                                             nodes[ iSplit < iBestQuad ? i4 : i3 ]));
8823     }
8824
8825     const SMDS_MeshNode* newNodes[ 4 ];
8826     newNodes[ 0 ] = linkNodes[ i1 ];
8827     newNodes[ 1 ] = linkNodes[ i2 ];
8828     newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
8829     newNodes[ 3 ] = nodes[ i4 ];
8830     if (iSplit == iBestQuad)
8831       newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] ));
8832     else
8833       newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] ));
8834
8835   } // end if(!theFace->IsQuadratic())
8836
8837   else { // theFace is quadratic
8838     // we have to split theFace on simple triangles and one simple quadrangle
8839     int tmp = il1/2;
8840     int nbshift = tmp*2;
8841     // shift nodes in nodes[] by nbshift
8842     int i,j;
8843     for(i=0; i<nbshift; i++) {
8844       const SMDS_MeshNode* n = nodes[0];
8845       for(j=0; j<nbFaceNodes-1; j++) {
8846         nodes[j] = nodes[j+1];
8847       }
8848       nodes[nbFaceNodes-1] = n;
8849     }
8850     il1 = il1 - nbshift;
8851     // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
8852     //   n0      n1     n2    n0      n1     n2
8853     //     +-----+-----+        +-----+-----+
8854     //      \         /         |           |
8855     //       \       /          |           |
8856     //      n5+     +n3       n7+           +n3
8857     //         \   /            |           |
8858     //          \ /             |           |
8859     //           +              +-----+-----+
8860     //           n4           n6      n5     n4
8861
8862     // create new elements
8863     int n1,n2,n3;
8864     if ( nbFaceNodes == 6 ) { // quadratic triangle
8865       newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8866       if ( theFace->IsMediumNode(nodes[il1]) ) {
8867         // create quadrangle
8868         newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[5] ));
8869         n1 = 1;
8870         n2 = 2;
8871         n3 = 3;
8872       }
8873       else {
8874         // create quadrangle
8875         newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[5] ));
8876         n1 = 0;
8877         n2 = 1;
8878         n3 = 5;
8879       }
8880     }
8881     else { // nbFaceNodes==8 - quadratic quadrangle
8882       newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8883       newElems.push_back( aMesh->AddFace( nodes[5], nodes[6], nodes[7] ));
8884       newElems.push_back( aMesh->AddFace( nodes[5], nodes[7], nodes[3] ));
8885       if ( theFace->IsMediumNode( nodes[ il1 ])) {
8886         // create quadrangle
8887         newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[7] ));
8888         n1 = 1;
8889         n2 = 2;
8890         n3 = 3;
8891       }
8892       else {
8893         // create quadrangle
8894         newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[7] ));
8895         n1 = 0;
8896         n2 = 1;
8897         n3 = 7;
8898       }
8899     }
8900     // create needed triangles using n1,n2,n3 and inserted nodes
8901     int nbn = 2 + aNodesToInsert.size();
8902     vector<const SMDS_MeshNode*> aNodes(nbn);
8903     aNodes[0    ] = nodes[n1];
8904     aNodes[nbn-1] = nodes[n2];
8905     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8906     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8907       aNodes[iNode++] = *nIt;
8908     }
8909     for ( i = 1; i < nbn; i++ )
8910       newElems.push_back( aMesh->AddFace( aNodes[i-1], aNodes[i], nodes[n3] ));
8911   }
8912
8913   // remove the old face
8914   for ( size_t i = 0; i < newElems.size(); ++i )
8915     if ( newElems[i] )
8916     {
8917       aMesh->SetMeshElementOnShape( newElems[i], theFace->getshapeId() );
8918       myLastCreatedElems.push_back( newElems[i] );
8919     }
8920   ReplaceElemInGroups( theFace, newElems, aMesh );
8921   aMesh->RemoveElement(theFace);
8922
8923 } // InsertNodesIntoLink()
8924
8925 //=======================================================================
8926 //function : UpdateVolumes
8927 //purpose  :
8928 //=======================================================================
8929
8930 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode*        theBetweenNode1,
8931                                       const SMDS_MeshNode*        theBetweenNode2,
8932                                       list<const SMDS_MeshNode*>& theNodesToInsert)
8933 {
8934   ClearLastCreated();
8935
8936   SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
8937   while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
8938     const SMDS_MeshElement* elem = invElemIt->next();
8939
8940     // check, if current volume has link theBetweenNode1 - theBetweenNode2
8941     SMDS_VolumeTool aVolume (elem);
8942     if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
8943       continue;
8944
8945     // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
8946     int iface, nbFaces = aVolume.NbFaces();
8947     vector<const SMDS_MeshNode *> poly_nodes;
8948     vector<int> quantities (nbFaces);
8949
8950     for (iface = 0; iface < nbFaces; iface++) {
8951       int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
8952       // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
8953       const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
8954
8955       for (int inode = 0; inode < nbFaceNodes; inode++) {
8956         poly_nodes.push_back(faceNodes[inode]);
8957
8958         if (nbInserted == 0) {
8959           if (faceNodes[inode] == theBetweenNode1) {
8960             if (faceNodes[inode + 1] == theBetweenNode2) {
8961               nbInserted = theNodesToInsert.size();
8962
8963               // add nodes to insert
8964               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
8965               for (; nIt != theNodesToInsert.end(); nIt++) {
8966                 poly_nodes.push_back(*nIt);
8967               }
8968             }
8969           }
8970           else if (faceNodes[inode] == theBetweenNode2) {
8971             if (faceNodes[inode + 1] == theBetweenNode1) {
8972               nbInserted = theNodesToInsert.size();
8973
8974               // add nodes to insert in reversed order
8975               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
8976               nIt--;
8977               for (; nIt != theNodesToInsert.begin(); nIt--) {
8978                 poly_nodes.push_back(*nIt);
8979               }
8980               poly_nodes.push_back(*nIt);
8981             }
8982           }
8983           else {
8984           }
8985         }
8986       }
8987       quantities[iface] = nbFaceNodes + nbInserted;
8988     }
8989
8990     // Replace the volume
8991     SMESHDS_Mesh *aMesh = GetMeshDS();
8992
8993     if ( SMDS_MeshElement* newElem = aMesh->AddPolyhedralVolume( poly_nodes, quantities ))
8994     {
8995       aMesh->SetMeshElementOnShape( newElem, elem->getshapeId() );
8996       myLastCreatedElems.push_back( newElem );
8997       ReplaceElemInGroups( elem, newElem, aMesh );
8998     }
8999     aMesh->RemoveElement( elem );
9000   }
9001 }
9002
9003 namespace
9004 {
9005   //================================================================================
9006   /*!
9007    * \brief Transform any volume into data of SMDSEntity_Polyhedra
9008    */
9009   //================================================================================
9010
9011   void volumeToPolyhedron( const SMDS_MeshElement*         elem,
9012                            vector<const SMDS_MeshNode *> & nodes,
9013                            vector<int> &                   nbNodeInFaces )
9014   {
9015     nodes.clear();
9016     nbNodeInFaces.clear();
9017     SMDS_VolumeTool vTool ( elem );
9018     for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
9019     {
9020       const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
9021       nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
9022       nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
9023     }
9024   }
9025 }
9026
9027 //=======================================================================
9028 /*!
9029  * \brief Convert elements contained in a sub-mesh to quadratic
9030  * \return int - nb of checked elements
9031  */
9032 //=======================================================================
9033
9034 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh *   theSm,
9035                                              SMESH_MesherHelper& theHelper,
9036                                              const bool          theForce3d)
9037 {
9038   //MESSAGE("convertElemToQuadratic");
9039   int nbElem = 0;
9040   if( !theSm ) return nbElem;
9041
9042   vector<int> nbNodeInFaces;
9043   vector<const SMDS_MeshNode *> nodes;
9044   SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
9045   while(ElemItr->more())
9046   {
9047     nbElem++;
9048     const SMDS_MeshElement* elem = ElemItr->next();
9049     if( !elem ) continue;
9050
9051     // analyse a necessity of conversion
9052     const SMDSAbs_ElementType aType = elem->GetType();
9053     if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
9054       continue;
9055     const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
9056     bool hasCentralNodes = false;
9057     if ( elem->IsQuadratic() )
9058     {
9059       bool alreadyOK;
9060       switch ( aGeomType ) {
9061       case SMDSEntity_Quad_Triangle:
9062       case SMDSEntity_Quad_Quadrangle:
9063       case SMDSEntity_Quad_Hexa:
9064       case SMDSEntity_Quad_Penta:
9065         alreadyOK = !theHelper.GetIsBiQuadratic(); break;
9066
9067       case SMDSEntity_BiQuad_Triangle:
9068       case SMDSEntity_BiQuad_Quadrangle:
9069       case SMDSEntity_TriQuad_Hexa:
9070       case SMDSEntity_BiQuad_Penta:
9071         alreadyOK = theHelper.GetIsBiQuadratic();
9072         hasCentralNodes = true;
9073         break;
9074       default:
9075         alreadyOK = true;
9076       }
9077       // take into account already present medium nodes
9078       switch ( aType ) {
9079       case SMDSAbs_Volume:
9080         theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
9081       case SMDSAbs_Face:
9082         theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
9083       case SMDSAbs_Edge:
9084         theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
9085       default:;
9086       }
9087       if ( alreadyOK )
9088         continue;
9089     }
9090     // get elem data needed to re-create it
9091     //
9092     const int id      = elem->GetID();
9093     const int nbNodes = elem->NbCornerNodes();
9094     nodes.assign(elem->begin_nodes(), elem->end_nodes());
9095     if ( aGeomType == SMDSEntity_Polyhedra )
9096       nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
9097     else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
9098       volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9099
9100     // remove a linear element
9101     GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9102
9103     // remove central nodes of biquadratic elements (biquad->quad conversion)
9104     if ( hasCentralNodes )
9105       for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
9106         if ( nodes[i]->NbInverseElements() == 0 )
9107           GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
9108
9109     const SMDS_MeshElement* NewElem = 0;
9110
9111     switch( aType )
9112     {
9113     case SMDSAbs_Edge :
9114     {
9115       NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9116       break;
9117     }
9118     case SMDSAbs_Face :
9119     {
9120       switch(nbNodes)
9121       {
9122       case 3:
9123         NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9124         break;
9125       case 4:
9126         NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9127         break;
9128       default:
9129         NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9130       }
9131       break;
9132     }
9133     case SMDSAbs_Volume :
9134     {
9135       switch( aGeomType )
9136       {
9137       case SMDSEntity_Tetra:
9138         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9139         break;
9140       case SMDSEntity_Pyramid:
9141         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9142         break;
9143       case SMDSEntity_Penta:
9144       case SMDSEntity_Quad_Penta:
9145       case SMDSEntity_BiQuad_Penta:
9146         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9147         break;
9148       case SMDSEntity_Hexa:
9149       case SMDSEntity_Quad_Hexa:
9150       case SMDSEntity_TriQuad_Hexa:
9151         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9152                                       nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9153         break;
9154       case SMDSEntity_Hexagonal_Prism:
9155       default:
9156         NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9157       }
9158       break;
9159     }
9160     default :
9161       continue;
9162     }
9163     ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9164     if( NewElem && NewElem->getshapeId() < 1 )
9165       theSm->AddElement( NewElem );
9166   }
9167   return nbElem;
9168 }
9169 //=======================================================================
9170 //function : ConvertToQuadratic
9171 //purpose  :
9172 //=======================================================================
9173
9174 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
9175 {
9176   //MESSAGE("ConvertToQuadratic "<< theForce3d << " " << theToBiQuad);
9177   SMESHDS_Mesh* meshDS = GetMeshDS();
9178
9179   SMESH_MesherHelper aHelper(*myMesh);
9180
9181   aHelper.SetIsQuadratic( true );
9182   aHelper.SetIsBiQuadratic( theToBiQuad );
9183   aHelper.SetElementsOnShape(true);
9184   aHelper.ToFixNodeParameters( true );
9185
9186   // convert elements assigned to sub-meshes
9187   int nbCheckedElems = 0;
9188   if ( myMesh->HasShapeToMesh() )
9189   {
9190     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9191     {
9192       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9193       while ( smIt->more() ) {
9194         SMESH_subMesh* sm = smIt->next();
9195         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9196           aHelper.SetSubShape( sm->GetSubShape() );
9197           nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9198         }
9199       }
9200     }
9201   }
9202
9203   // convert elements NOT assigned to sub-meshes
9204   int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9205   if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
9206   {
9207     aHelper.SetElementsOnShape(false);
9208     SMESHDS_SubMesh *smDS = 0;
9209
9210     // convert edges
9211     SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9212     while( aEdgeItr->more() )
9213     {
9214       const SMDS_MeshEdge* edge = aEdgeItr->next();
9215       if ( !edge->IsQuadratic() )
9216       {
9217         int                  id = edge->GetID();
9218         const SMDS_MeshNode* n1 = edge->GetNode(0);
9219         const SMDS_MeshNode* n2 = edge->GetNode(1);
9220
9221         meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9222
9223         const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9224         ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9225       }
9226       else
9227       {
9228         aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
9229       }
9230     }
9231
9232     // convert faces
9233     SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9234     while( aFaceItr->more() )
9235     {
9236       const SMDS_MeshFace* face = aFaceItr->next();
9237       if ( !face ) continue;
9238       
9239       const SMDSAbs_EntityType type = face->GetEntityType();
9240       bool alreadyOK;
9241       switch( type )
9242       {
9243       case SMDSEntity_Quad_Triangle:
9244       case SMDSEntity_Quad_Quadrangle:
9245         alreadyOK = !theToBiQuad;
9246         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9247         break;
9248       case SMDSEntity_BiQuad_Triangle:
9249       case SMDSEntity_BiQuad_Quadrangle:
9250         alreadyOK = theToBiQuad;
9251         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9252         break;
9253       default: alreadyOK = false;
9254       }
9255       if ( alreadyOK )
9256         continue;
9257
9258       const int id = face->GetID();
9259       vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9260
9261       meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9262
9263       SMDS_MeshFace * NewFace = 0;
9264       switch( type )
9265       {
9266       case SMDSEntity_Triangle:
9267       case SMDSEntity_Quad_Triangle:
9268       case SMDSEntity_BiQuad_Triangle:
9269         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9270         if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
9271           GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
9272         break;
9273
9274       case SMDSEntity_Quadrangle:
9275       case SMDSEntity_Quad_Quadrangle:
9276       case SMDSEntity_BiQuad_Quadrangle:
9277         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9278         if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
9279           GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
9280         break;
9281
9282       default:;
9283         NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9284       }
9285       ReplaceElemInGroups( face, NewFace, GetMeshDS());
9286     }
9287
9288     // convert volumes
9289     vector<int> nbNodeInFaces;
9290     SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9291     while(aVolumeItr->more())
9292     {
9293       const SMDS_MeshVolume* volume = aVolumeItr->next();
9294       if ( !volume ) continue;
9295
9296       const SMDSAbs_EntityType type = volume->GetEntityType();
9297       if ( volume->IsQuadratic() )
9298       {
9299         bool alreadyOK;
9300         switch ( type )
9301         {
9302         case SMDSEntity_Quad_Hexa:    alreadyOK = !theToBiQuad; break;
9303         case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9304         case SMDSEntity_Quad_Penta:   alreadyOK = !theToBiQuad; break;
9305         case SMDSEntity_BiQuad_Penta: alreadyOK = theToBiQuad; break;
9306         default:                      alreadyOK = true;
9307         }
9308         if ( alreadyOK )
9309         {
9310           aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
9311           continue;
9312         }
9313       }
9314       const int id = volume->GetID();
9315       vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9316       if ( type == SMDSEntity_Polyhedra )
9317         nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
9318       else if ( type == SMDSEntity_Hexagonal_Prism )
9319         volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9320
9321       meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9322
9323       SMDS_MeshVolume * NewVolume = 0;
9324       switch ( type )
9325       {
9326       case SMDSEntity_Tetra:
9327         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9328         break;
9329       case SMDSEntity_Hexa:
9330       case SMDSEntity_Quad_Hexa:
9331       case SMDSEntity_TriQuad_Hexa:
9332         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9333                                       nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9334         for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
9335           if ( nodes[i]->NbInverseElements() == 0 )
9336             GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9337         break;
9338       case SMDSEntity_Pyramid:
9339         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9340                                       nodes[3], nodes[4], id, theForce3d);
9341         break;
9342       case SMDSEntity_Penta:
9343       case SMDSEntity_Quad_Penta:
9344       case SMDSEntity_BiQuad_Penta:
9345         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9346                                       nodes[3], nodes[4], nodes[5], id, theForce3d);
9347         for ( size_t i = 15; i < nodes.size(); ++i ) // rm central nodes
9348           if ( nodes[i]->NbInverseElements() == 0 )
9349             GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9350         break;
9351       case SMDSEntity_Hexagonal_Prism:
9352       default:
9353         NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9354       }
9355       ReplaceElemInGroups(volume, NewVolume, meshDS);
9356     }
9357   }
9358
9359   if ( !theForce3d )
9360   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9361     // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9362     // aHelper.FixQuadraticElements(myError);
9363     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9364   }
9365 }
9366
9367 //================================================================================
9368 /*!
9369  * \brief Makes given elements quadratic
9370  *  \param theForce3d - if true, the medium nodes will be placed in the middle of link
9371  *  \param theElements - elements to make quadratic
9372  */
9373 //================================================================================
9374
9375 void SMESH_MeshEditor::ConvertToQuadratic(const bool        theForce3d,
9376                                           TIDSortedElemSet& theElements,
9377                                           const bool        theToBiQuad)
9378 {
9379   if ( theElements.empty() ) return;
9380
9381   // we believe that all theElements are of the same type
9382   const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9383
9384   // get all nodes shared by theElements
9385   TIDSortedNodeSet allNodes;
9386   TIDSortedElemSet::iterator eIt = theElements.begin();
9387   for ( ; eIt != theElements.end(); ++eIt )
9388     allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9389
9390   // complete theElements with elements of lower dim whose all nodes are in allNodes
9391
9392   TIDSortedElemSet quadAdjacentElems    [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9393   TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9394   TIDSortedNodeSet::iterator nIt = allNodes.begin();
9395   for ( ; nIt != allNodes.end(); ++nIt )
9396   {
9397     const SMDS_MeshNode* n = *nIt;
9398     SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9399     while ( invIt->more() )
9400     {
9401       const SMDS_MeshElement*      e = invIt->next();
9402       const SMDSAbs_ElementType type = e->GetType();
9403       if ( e->IsQuadratic() )
9404       {
9405         quadAdjacentElems[ type ].insert( e );
9406
9407         bool alreadyOK;
9408         switch ( e->GetEntityType() ) {
9409         case SMDSEntity_Quad_Triangle:
9410         case SMDSEntity_Quad_Quadrangle:
9411         case SMDSEntity_Quad_Hexa:         alreadyOK = !theToBiQuad; break;
9412         case SMDSEntity_BiQuad_Triangle:
9413         case SMDSEntity_BiQuad_Quadrangle:
9414         case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; break;
9415         default:                           alreadyOK = true;
9416         }
9417         if ( alreadyOK )
9418           continue;
9419       }
9420       if ( type >= elemType )
9421         continue; // same type or more complex linear element
9422
9423       if ( !checkedAdjacentElems[ type ].insert( e ).second )
9424         continue; // e is already checked
9425
9426       // check nodes
9427       bool allIn = true;
9428       SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9429       while ( nodeIt->more() && allIn )
9430         allIn = allNodes.count( nodeIt->next() );
9431       if ( allIn )
9432         theElements.insert(e );
9433     }
9434   }
9435
9436   SMESH_MesherHelper helper(*myMesh);
9437   helper.SetIsQuadratic( true );
9438   helper.SetIsBiQuadratic( theToBiQuad );
9439
9440   // add links of quadratic adjacent elements to the helper
9441
9442   if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9443     for ( eIt  = quadAdjacentElems[SMDSAbs_Edge].begin();
9444           eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9445     {
9446       helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9447     }
9448   if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9449     for ( eIt  = quadAdjacentElems[SMDSAbs_Face].begin();
9450           eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9451     {
9452       helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9453     }
9454   if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9455     for ( eIt  = quadAdjacentElems[SMDSAbs_Volume].begin();
9456           eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9457     {
9458       helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9459     }
9460
9461   // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9462
9463   SMESHDS_Mesh*  meshDS = GetMeshDS();
9464   SMESHDS_SubMesh* smDS = 0;
9465   for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9466   {
9467     const SMDS_MeshElement* elem = *eIt;
9468
9469     bool alreadyOK;
9470     int nbCentralNodes = 0;
9471     switch ( elem->GetEntityType() ) {
9472       // linear convertible
9473     case SMDSEntity_Edge:
9474     case SMDSEntity_Triangle:
9475     case SMDSEntity_Quadrangle:
9476     case SMDSEntity_Tetra:
9477     case SMDSEntity_Pyramid:
9478     case SMDSEntity_Hexa:
9479     case SMDSEntity_Penta:             alreadyOK = false;       nbCentralNodes = 0; break;
9480       // quadratic that can become bi-quadratic
9481     case SMDSEntity_Quad_Triangle:
9482     case SMDSEntity_Quad_Quadrangle:
9483     case SMDSEntity_Quad_Hexa:         alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9484       // bi-quadratic
9485     case SMDSEntity_BiQuad_Triangle:
9486     case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9487     case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9488       // the rest
9489     default:                           alreadyOK = true;
9490     }
9491     if ( alreadyOK ) continue;
9492
9493     const SMDSAbs_ElementType type = elem->GetType();
9494     const int                   id = elem->GetID();
9495     const int              nbNodes = elem->NbCornerNodes();
9496     vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9497
9498     helper.SetSubShape( elem->getshapeId() );
9499
9500     if ( !smDS || !smDS->Contains( elem ))
9501       smDS = meshDS->MeshElements( elem->getshapeId() );
9502     meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9503
9504     SMDS_MeshElement * newElem = 0;
9505     switch( nbNodes )
9506     {
9507     case 4: // cases for most frequently used element types go first (for optimization)
9508       if ( type == SMDSAbs_Volume )
9509         newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9510       else
9511         newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9512       break;
9513     case 8:
9514       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9515                                  nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9516       break;
9517     case 3:
9518       newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], id, theForce3d);
9519       break;
9520     case 2:
9521       newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9522       break;
9523     case 5:
9524       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9525                                  nodes[4], id, theForce3d);
9526       break;
9527     case 6:
9528       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9529                                  nodes[4], nodes[5], id, theForce3d);
9530       break;
9531     default:;
9532     }
9533     ReplaceElemInGroups( elem, newElem, meshDS);
9534     if( newElem && smDS )
9535       smDS->AddElement( newElem );
9536
9537     // remove central nodes
9538     for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9539       if ( nodes[i]->NbInverseElements() == 0 )
9540         meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9541
9542   } // loop on theElements
9543
9544   if ( !theForce3d )
9545   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9546     // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9547     // helper.FixQuadraticElements( myError );
9548     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9549   }
9550 }
9551
9552 //=======================================================================
9553 /*!
9554  * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9555  * \return int - nb of checked elements
9556  */
9557 //=======================================================================
9558
9559 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh *    theSm,
9560                                      SMDS_ElemIteratorPtr theItr,
9561                                      const int            theShapeID)
9562 {
9563   int nbElem = 0;
9564   SMESHDS_Mesh* meshDS = GetMeshDS();
9565   ElemFeatures elemType;
9566   vector<const SMDS_MeshNode *> nodes;
9567
9568   while( theItr->more() )
9569   {
9570     const SMDS_MeshElement* elem = theItr->next();
9571     nbElem++;
9572     if( elem && elem->IsQuadratic())
9573     {
9574       // get elem data
9575       int nbCornerNodes = elem->NbCornerNodes();
9576       nodes.assign( elem->begin_nodes(), elem->end_nodes() );
9577
9578       elemType.Init( elem, /*basicOnly=*/false ).SetID( elem->GetID() ).SetQuad( false );
9579
9580       //remove a quadratic element
9581       if ( !theSm || !theSm->Contains( elem ))
9582         theSm = meshDS->MeshElements( elem->getshapeId() );
9583       meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9584
9585       // remove medium nodes
9586       for ( size_t i = nbCornerNodes; i < nodes.size(); ++i )
9587         if ( nodes[i]->NbInverseElements() == 0 )
9588           meshDS->RemoveFreeNode( nodes[i], theSm );
9589
9590       // add a linear element
9591       nodes.resize( nbCornerNodes );
9592       SMDS_MeshElement * newElem = AddElement( nodes, elemType );
9593       ReplaceElemInGroups(elem, newElem, meshDS);
9594       if( theSm && newElem )
9595         theSm->AddElement( newElem );
9596     }
9597   }
9598   return nbElem;
9599 }
9600
9601 //=======================================================================
9602 //function : ConvertFromQuadratic
9603 //purpose  :
9604 //=======================================================================
9605
9606 bool SMESH_MeshEditor::ConvertFromQuadratic()
9607 {
9608   int nbCheckedElems = 0;
9609   if ( myMesh->HasShapeToMesh() )
9610   {
9611     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9612     {
9613       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9614       while ( smIt->more() ) {
9615         SMESH_subMesh* sm = smIt->next();
9616         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9617           nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9618       }
9619     }
9620   }
9621
9622   int totalNbElems =
9623     GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9624   if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9625   {
9626     SMESHDS_SubMesh *aSM = 0;
9627     removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9628   }
9629
9630   return true;
9631 }
9632
9633 namespace
9634 {
9635   //================================================================================
9636   /*!
9637    * \brief Return true if all medium nodes of the element are in the node set
9638    */
9639   //================================================================================
9640
9641   bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9642   {
9643     for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9644       if ( !nodeSet.count( elem->GetNode(i) ))
9645         return false;
9646     return true;
9647   }
9648 }
9649
9650 //================================================================================
9651 /*!
9652  * \brief Makes given elements linear
9653  */
9654 //================================================================================
9655
9656 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9657 {
9658   if ( theElements.empty() ) return;
9659
9660   // collect IDs of medium nodes of theElements; some of these nodes will be removed
9661   set<int> mediumNodeIDs;
9662   TIDSortedElemSet::iterator eIt = theElements.begin();
9663   for ( ; eIt != theElements.end(); ++eIt )
9664   {
9665     const SMDS_MeshElement* e = *eIt;
9666     for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9667       mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9668   }
9669
9670   // replace given elements by linear ones
9671   SMDS_ElemIteratorPtr elemIt = SMESHUtils::elemSetIterator( theElements );
9672   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9673
9674   // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9675   // except those elements sharing medium nodes of quadratic element whose medium nodes
9676   // are not all in mediumNodeIDs
9677
9678   // get remaining medium nodes
9679   TIDSortedNodeSet mediumNodes;
9680   set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9681   for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9682     if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9683       mediumNodes.insert( mediumNodes.end(), n );
9684
9685   // find more quadratic elements to convert
9686   TIDSortedElemSet moreElemsToConvert;
9687   TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9688   for ( ; nIt != mediumNodes.end(); ++nIt )
9689   {
9690     SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9691     while ( invIt->more() )
9692     {
9693       const SMDS_MeshElement* e = invIt->next();
9694       if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9695       {
9696         // find a more complex element including e and
9697         // whose medium nodes are not in mediumNodes
9698         bool complexFound = false;
9699         for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9700         {
9701           SMDS_ElemIteratorPtr invIt2 =
9702             (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9703           while ( invIt2->more() )
9704           {
9705             const SMDS_MeshElement* eComplex = invIt2->next();
9706             if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9707             {
9708               int nbCommonNodes = SMESH_MeshAlgos::GetCommonNodes( e, eComplex ).size();
9709               if ( nbCommonNodes == e->NbNodes())
9710               {
9711                 complexFound = true;
9712                 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9713                 break;
9714               }
9715             }
9716           }
9717         }
9718         if ( !complexFound )
9719           moreElemsToConvert.insert( e );
9720       }
9721     }
9722   }
9723   elemIt = SMESHUtils::elemSetIterator( moreElemsToConvert );
9724   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9725 }
9726
9727 //=======================================================================
9728 //function : SewSideElements
9729 //purpose  :
9730 //=======================================================================
9731
9732 SMESH_MeshEditor::Sew_Error
9733 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet&    theSide1,
9734                                    TIDSortedElemSet&    theSide2,
9735                                    const SMDS_MeshNode* theFirstNode1,
9736                                    const SMDS_MeshNode* theFirstNode2,
9737                                    const SMDS_MeshNode* theSecondNode1,
9738                                    const SMDS_MeshNode* theSecondNode2)
9739 {
9740   ClearLastCreated();
9741
9742   if ( theSide1.size() != theSide2.size() )
9743     return SEW_DIFF_NB_OF_ELEMENTS;
9744
9745   Sew_Error aResult = SEW_OK;
9746   // Algo:
9747   // 1. Build set of faces representing each side
9748   // 2. Find which nodes of the side 1 to merge with ones on the side 2
9749   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9750
9751   // =======================================================================
9752   // 1. Build set of faces representing each side:
9753   // =======================================================================
9754   // a. build set of nodes belonging to faces
9755   // b. complete set of faces: find missing faces whose nodes are in set of nodes
9756   // c. create temporary faces representing side of volumes if correspondent
9757   //    face does not exist
9758
9759   SMESHDS_Mesh* aMesh = GetMeshDS();
9760   // TODO algorithm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9761   //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9762   TIDSortedElemSet             faceSet1, faceSet2;
9763   set<const SMDS_MeshElement*> volSet1,  volSet2;
9764   set<const SMDS_MeshNode*>    nodeSet1, nodeSet2;
9765   TIDSortedElemSet             * faceSetPtr[] = { &faceSet1, &faceSet2 };
9766   set<const SMDS_MeshElement*> *  volSetPtr[] = { &volSet1,  &volSet2  };
9767   set<const SMDS_MeshNode*>    * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9768   TIDSortedElemSet             * elemSetPtr[] = { &theSide1, &theSide2 };
9769   int iSide, iFace, iNode;
9770
9771   list<const SMDS_MeshElement* > tempFaceList;
9772   for ( iSide = 0; iSide < 2; iSide++ ) {
9773     set<const SMDS_MeshNode*>    * nodeSet = nodeSetPtr[ iSide ];
9774     TIDSortedElemSet             * elemSet = elemSetPtr[ iSide ];
9775     TIDSortedElemSet             * faceSet = faceSetPtr[ iSide ];
9776     set<const SMDS_MeshElement*> * volSet  = volSetPtr [ iSide ];
9777     set<const SMDS_MeshElement*>::iterator vIt;
9778     TIDSortedElemSet::iterator eIt;
9779     set<const SMDS_MeshNode*>::iterator    nIt;
9780
9781     // check that given nodes belong to given elements
9782     const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9783     const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9784     int firstIndex = -1, secondIndex = -1;
9785     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9786       const SMDS_MeshElement* elem = *eIt;
9787       if ( firstIndex  < 0 ) firstIndex  = elem->GetNodeIndex( n1 );
9788       if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9789       if ( firstIndex > -1 && secondIndex > -1 ) break;
9790     }
9791     if ( firstIndex < 0 || secondIndex < 0 ) {
9792       // we can simply return until temporary faces created
9793       return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9794     }
9795
9796     // -----------------------------------------------------------
9797     // 1a. Collect nodes of existing faces
9798     //     and build set of face nodes in order to detect missing
9799     //     faces corresponding to sides of volumes
9800     // -----------------------------------------------------------
9801
9802     set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9803
9804     // loop on the given element of a side
9805     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9806       //const SMDS_MeshElement* elem = *eIt;
9807       const SMDS_MeshElement* elem = *eIt;
9808       if ( elem->GetType() == SMDSAbs_Face ) {
9809         faceSet->insert( elem );
9810         set <const SMDS_MeshNode*> faceNodeSet;
9811         SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9812         while ( nodeIt->more() ) {
9813           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9814           nodeSet->insert( n );
9815           faceNodeSet.insert( n );
9816         }
9817         setOfFaceNodeSet.insert( faceNodeSet );
9818       }
9819       else if ( elem->GetType() == SMDSAbs_Volume )
9820         volSet->insert( elem );
9821     }
9822     // ------------------------------------------------------------------------------
9823     // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9824     // ------------------------------------------------------------------------------
9825
9826     for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9827       SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9828       while ( fIt->more() ) { // loop on faces sharing a node
9829         const SMDS_MeshElement* f = fIt->next();
9830         if ( faceSet->find( f ) == faceSet->end() ) {
9831           // check if all nodes are in nodeSet and
9832           // complete setOfFaceNodeSet if they are
9833           set <const SMDS_MeshNode*> faceNodeSet;
9834           SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9835           bool allInSet = true;
9836           while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9837             const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9838             if ( nodeSet->find( n ) == nodeSet->end() )
9839               allInSet = false;
9840             else
9841               faceNodeSet.insert( n );
9842           }
9843           if ( allInSet ) {
9844             faceSet->insert( f );
9845             setOfFaceNodeSet.insert( faceNodeSet );
9846           }
9847         }
9848       }
9849     }
9850
9851     // -------------------------------------------------------------------------
9852     // 1c. Create temporary faces representing sides of volumes if correspondent
9853     //     face does not exist
9854     // -------------------------------------------------------------------------
9855
9856     if ( !volSet->empty() ) {
9857       //int nodeSetSize = nodeSet->size();
9858
9859       // loop on given volumes
9860       for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
9861         SMDS_VolumeTool vol (*vIt);
9862         // loop on volume faces: find free faces
9863         // --------------------------------------
9864         list<const SMDS_MeshElement* > freeFaceList;
9865         for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
9866           if ( !vol.IsFreeFace( iFace ))
9867             continue;
9868           // check if there is already a face with same nodes in a face set
9869           const SMDS_MeshElement* aFreeFace = 0;
9870           const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
9871           int nbNodes = vol.NbFaceNodes( iFace );
9872           set <const SMDS_MeshNode*> faceNodeSet;
9873           vol.GetFaceNodes( iFace, faceNodeSet );
9874           bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
9875           if ( isNewFace ) {
9876             // no such a face is given but it still can exist, check it
9877             vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
9878             aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
9879           }
9880           if ( !aFreeFace ) {
9881             // create a temporary face
9882             if ( nbNodes == 3 ) {
9883               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
9884               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
9885             }
9886             else if ( nbNodes == 4 ) {
9887               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9888               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9889             }
9890             else {
9891               vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9892               //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
9893               aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
9894             }
9895             if ( aFreeFace )
9896               tempFaceList.push_back( aFreeFace );
9897           }
9898
9899           if ( aFreeFace )
9900             freeFaceList.push_back( aFreeFace );
9901
9902         } // loop on faces of a volume
9903
9904         // choose one of several free faces of a volume
9905         // --------------------------------------------
9906         if ( freeFaceList.size() > 1 ) {
9907           // choose a face having max nb of nodes shared by other elems of a side
9908           int maxNbNodes = -1;
9909           list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
9910           while ( fIt != freeFaceList.end() ) { // loop on free faces
9911             int nbSharedNodes = 0;
9912             SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9913             while ( nodeIt->more() ) { // loop on free face nodes
9914               const SMDS_MeshNode* n =
9915                 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9916               SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
9917               while ( invElemIt->more() ) {
9918                 const SMDS_MeshElement* e = invElemIt->next();
9919                 nbSharedNodes += faceSet->count( e );
9920                 nbSharedNodes += elemSet->count( e );
9921               }
9922             }
9923             if ( nbSharedNodes > maxNbNodes ) {
9924               maxNbNodes = nbSharedNodes;
9925               freeFaceList.erase( freeFaceList.begin(), fIt++ );
9926             }
9927             else if ( nbSharedNodes == maxNbNodes ) {
9928               fIt++;
9929             }
9930             else {
9931               freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
9932             }
9933           }
9934           if ( freeFaceList.size() > 1 )
9935           {
9936             // could not choose one face, use another way
9937             // choose a face most close to the bary center of the opposite side
9938             gp_XYZ aBC( 0., 0., 0. );
9939             set <const SMDS_MeshNode*> addedNodes;
9940             TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
9941             eIt = elemSet2->begin();
9942             for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
9943               SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
9944               while ( nodeIt->more() ) { // loop on free face nodes
9945                 const SMDS_MeshNode* n =
9946                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9947                 if ( addedNodes.insert( n ).second )
9948                   aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
9949               }
9950             }
9951             aBC /= addedNodes.size();
9952             double minDist = DBL_MAX;
9953             fIt = freeFaceList.begin();
9954             while ( fIt != freeFaceList.end() ) { // loop on free faces
9955               double dist = 0;
9956               SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9957               while ( nodeIt->more() ) { // loop on free face nodes
9958                 const SMDS_MeshNode* n =
9959                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9960                 gp_XYZ p( n->X(),n->Y(),n->Z() );
9961                 dist += ( aBC - p ).SquareModulus();
9962               }
9963               if ( dist < minDist ) {
9964                 minDist = dist;
9965                 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9966               }
9967               else
9968                 fIt = freeFaceList.erase( fIt++ );
9969             }
9970           }
9971         } // choose one of several free faces of a volume
9972
9973         if ( freeFaceList.size() == 1 ) {
9974           const SMDS_MeshElement* aFreeFace = freeFaceList.front();
9975           faceSet->insert( aFreeFace );
9976           // complete a node set with nodes of a found free face
9977           //           for ( iNode = 0; iNode < ; iNode++ )
9978           //             nodeSet->insert( fNodes[ iNode ] );
9979         }
9980
9981       } // loop on volumes of a side
9982
9983       //       // complete a set of faces if new nodes in a nodeSet appeared
9984       //       // ----------------------------------------------------------
9985       //       if ( nodeSetSize != nodeSet->size() ) {
9986       //         for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9987       //           SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9988       //           while ( fIt->more() ) { // loop on faces sharing a node
9989       //             const SMDS_MeshElement* f = fIt->next();
9990       //             if ( faceSet->find( f ) == faceSet->end() ) {
9991       //               // check if all nodes are in nodeSet and
9992       //               // complete setOfFaceNodeSet if they are
9993       //               set <const SMDS_MeshNode*> faceNodeSet;
9994       //               SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9995       //               bool allInSet = true;
9996       //               while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9997       //                 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9998       //                 if ( nodeSet->find( n ) == nodeSet->end() )
9999       //                   allInSet = false;
10000       //                 else
10001       //                   faceNodeSet.insert( n );
10002       //               }
10003       //               if ( allInSet ) {
10004       //                 faceSet->insert( f );
10005       //                 setOfFaceNodeSet.insert( faceNodeSet );
10006       //               }
10007       //             }
10008       //           }
10009       //         }
10010       //       }
10011     } // Create temporary faces, if there are volumes given
10012   } // loop on sides
10013
10014   if ( faceSet1.size() != faceSet2.size() ) {
10015     // delete temporary faces: they are in reverseElements of actual nodes
10016     //    SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10017     //    while ( tmpFaceIt->more() )
10018     //      aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10019     //    list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10020     //    for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10021     //      aMesh->RemoveElement(*tmpFaceIt);
10022     MESSAGE("Diff nb of faces");
10023     return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10024   }
10025
10026   // ============================================================
10027   // 2. Find nodes to merge:
10028   //              bind a node to remove to a node to put instead
10029   // ============================================================
10030
10031   TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
10032   if ( theFirstNode1 != theFirstNode2 )
10033     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10034   if ( theSecondNode1 != theSecondNode2 )
10035     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10036
10037   LinkID_Gen aLinkID_Gen( GetMeshDS() );
10038   set< long > linkIdSet; // links to process
10039   linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
10040
10041   typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
10042   list< NLink > linkList[2];
10043   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10044   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10045   // loop on links in linkList; find faces by links and append links
10046   // of the found faces to linkList
10047   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10048   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
10049   {
10050     NLink link[] = { *linkIt[0], *linkIt[1] };
10051     long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
10052     if ( !linkIdSet.count( linkID ) )
10053       continue;
10054
10055     // by links, find faces in the face sets,
10056     // and find indices of link nodes in the found faces;
10057     // in a face set, there is only one or no face sharing a link
10058     // ---------------------------------------------------------------
10059
10060     const SMDS_MeshElement* face[] = { 0, 0 };
10061     vector<const SMDS_MeshNode*> fnodes[2];
10062     int iLinkNode[2][2];
10063     TIDSortedElemSet avoidSet;
10064     for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10065       const SMDS_MeshNode* n1 = link[iSide].first;
10066       const SMDS_MeshNode* n2 = link[iSide].second;
10067       //cout << "Side " << iSide << " ";
10068       //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
10069       // find a face by two link nodes
10070       face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
10071                                                       *faceSetPtr[ iSide ], avoidSet,
10072                                                       &iLinkNode[iSide][0],
10073                                                       &iLinkNode[iSide][1] );
10074       if ( face[ iSide ])
10075       {
10076         //cout << " F " << face[ iSide]->GetID() <<endl;
10077         faceSetPtr[ iSide ]->erase( face[ iSide ]);
10078         // put face nodes to fnodes
10079         if ( face[ iSide ]->IsQuadratic() )
10080         {
10081           // use interlaced nodes iterator
10082           const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>( face[ iSide ]);
10083           if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
10084           SMDS_ElemIteratorPtr nIter = F->interlacedNodesElemIterator();
10085           while ( nIter->more() )
10086             fnodes[ iSide ].push_back( cast2Node( nIter->next() ));
10087         }
10088         else
10089         {
10090           fnodes[ iSide ].assign( face[ iSide ]->begin_nodes(),
10091                                   face[ iSide ]->end_nodes() );
10092         }
10093         fnodes[ iSide ].push_back( fnodes[ iSide ].front());
10094       }
10095     }
10096
10097     // check similarity of elements of the sides
10098     if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10099       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10100       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10101         aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10102       }
10103       else {
10104         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10105       }
10106       break; // do not return because it's necessary to remove tmp faces
10107     }
10108
10109     // set nodes to merge
10110     // -------------------
10111
10112     if ( face[0] && face[1] )  {
10113       const int nbNodes = face[0]->NbNodes();
10114       if ( nbNodes != face[1]->NbNodes() ) {
10115         MESSAGE("Diff nb of face nodes");
10116         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10117         break; // do not return because it s necessary to remove tmp faces
10118       }
10119       bool reverse[] = { false, false }; // order of nodes in the link
10120       for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10121         // analyse link orientation in faces
10122         int i1 = iLinkNode[ iSide ][ 0 ];
10123         int i2 = iLinkNode[ iSide ][ 1 ];
10124         reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10125       }
10126       int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
10127       int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
10128       for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
10129       {
10130         nReplaceMap.insert  ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
10131                                           fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
10132       }
10133
10134       // add other links of the faces to linkList
10135       // -----------------------------------------
10136
10137       for ( iNode = 0; iNode < nbNodes; iNode++ )  {
10138         linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
10139         pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10140         if ( !iter_isnew.second ) { // already in a set: no need to process
10141           linkIdSet.erase( iter_isnew.first );
10142         }
10143         else // new in set == encountered for the first time: add
10144         {
10145           const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
10146           const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
10147           linkList[0].push_back ( NLink( n1, n2 ));
10148           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10149         }
10150       }
10151     } // 2 faces found
10152
10153     if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
10154       break;
10155
10156   } // loop on link lists
10157
10158   if ( aResult == SEW_OK &&
10159        ( //linkIt[0] != linkList[0].end() ||
10160         !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10161     MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10162              " " << (faceSetPtr[1]->empty()));
10163     aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10164   }
10165
10166   // ====================================================================
10167   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10168   // ====================================================================
10169
10170   // delete temporary faces
10171   //  SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10172   //  while ( tmpFaceIt->more() )
10173   //    aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10174   list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10175   for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10176     aMesh->RemoveElement(*tmpFaceIt);
10177
10178   if ( aResult != SEW_OK)
10179     return aResult;
10180
10181   list< int > nodeIDsToRemove;
10182   vector< const SMDS_MeshNode*> nodes;
10183   ElemFeatures elemType;
10184
10185   // loop on nodes replacement map
10186   TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10187   for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10188     if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second )
10189     {
10190       const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10191       nodeIDsToRemove.push_back( nToRemove->GetID() );
10192       // loop on elements sharing nToRemove
10193       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10194       while ( invElemIt->more() ) {
10195         const SMDS_MeshElement* e = invElemIt->next();
10196         // get a new suite of nodes: make replacement
10197         int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10198         nodes.resize( nbNodes );
10199         SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10200         while ( nIt->more() ) {
10201           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
10202           nnIt = nReplaceMap.find( n );
10203           if ( nnIt != nReplaceMap.end() ) {
10204             nbReplaced++;
10205             n = (*nnIt).second;
10206           }
10207           nodes[ i++ ] = n;
10208         }
10209         //       if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10210         //         elemIDsToRemove.push_back( e->GetID() );
10211         //       else
10212         if ( nbReplaced )
10213         {
10214           elemType.Init( e, /*basicOnly=*/false ).SetID( e->GetID() );
10215           aMesh->RemoveElement( e );
10216
10217           if ( SMDS_MeshElement* newElem = this->AddElement( nodes, elemType ))
10218           {
10219             AddToSameGroups( newElem, e, aMesh );
10220             if ( int aShapeId = e->getshapeId() )
10221               aMesh->SetMeshElementOnShape( newElem, aShapeId );
10222           }
10223         }
10224       }
10225     }
10226
10227   Remove( nodeIDsToRemove, true );
10228
10229   return aResult;
10230 }
10231
10232 //================================================================================
10233 /*!
10234  * \brief Find corresponding nodes in two sets of faces
10235  * \param theSide1 - first face set
10236  * \param theSide2 - second first face
10237  * \param theFirstNode1 - a boundary node of set 1
10238  * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10239  * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10240  * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10241  * \param nReplaceMap - output map of corresponding nodes
10242  * \return bool  - is a success or not
10243  */
10244 //================================================================================
10245
10246 #ifdef _DEBUG_
10247 //#define DEBUG_MATCHING_NODES
10248 #endif
10249
10250 SMESH_MeshEditor::Sew_Error
10251 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10252                                     set<const SMDS_MeshElement*>& theSide2,
10253                                     const SMDS_MeshNode*          theFirstNode1,
10254                                     const SMDS_MeshNode*          theFirstNode2,
10255                                     const SMDS_MeshNode*          theSecondNode1,
10256                                     const SMDS_MeshNode*          theSecondNode2,
10257                                     TNodeNodeMap &                nReplaceMap)
10258 {
10259   set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10260
10261   nReplaceMap.clear();
10262   if ( theFirstNode1 != theFirstNode2 )
10263     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10264   if ( theSecondNode1 != theSecondNode2 )
10265     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10266
10267   set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10268   linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10269
10270   list< NLink > linkList[2];
10271   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10272   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10273
10274   // loop on links in linkList; find faces by links and append links
10275   // of the found faces to linkList
10276   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10277   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10278     NLink link[] = { *linkIt[0], *linkIt[1] };
10279     if ( linkSet.find( link[0] ) == linkSet.end() )
10280       continue;
10281
10282     // by links, find faces in the face sets,
10283     // and find indices of link nodes in the found faces;
10284     // in a face set, there is only one or no face sharing a link
10285     // ---------------------------------------------------------------
10286
10287     const SMDS_MeshElement* face[] = { 0, 0 };
10288     list<const SMDS_MeshNode*> notLinkNodes[2];
10289     //bool reverse[] = { false, false }; // order of notLinkNodes
10290     int nbNodes[2];
10291     for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10292     {
10293       const SMDS_MeshNode* n1 = link[iSide].first;
10294       const SMDS_MeshNode* n2 = link[iSide].second;
10295       set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10296       set< const SMDS_MeshElement* > facesOfNode1;
10297       for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10298       {
10299         // during a loop of the first node, we find all faces around n1,
10300         // during a loop of the second node, we find one face sharing both n1 and n2
10301         const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10302         SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10303         while ( fIt->more() ) { // loop on faces sharing a node
10304           const SMDS_MeshElement* f = fIt->next();
10305           if (faceSet->find( f ) != faceSet->end() && // f is in face set
10306               ! facesOfNode1.insert( f ).second ) // f encounters twice
10307           {
10308             if ( face[ iSide ] ) {
10309               MESSAGE( "2 faces per link " );
10310               return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10311             }
10312             face[ iSide ] = f;
10313             faceSet->erase( f );
10314
10315             // get not link nodes
10316             int nbN = f->NbNodes();
10317             if ( f->IsQuadratic() )
10318               nbN /= 2;
10319             nbNodes[ iSide ] = nbN;
10320             list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10321             int i1 = f->GetNodeIndex( n1 );
10322             int i2 = f->GetNodeIndex( n2 );
10323             int iEnd = nbN, iBeg = -1, iDelta = 1;
10324             bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10325             if ( reverse ) {
10326               std::swap( iEnd, iBeg ); iDelta = -1;
10327             }
10328             int i = i2;
10329             while ( true ) {
10330               i += iDelta;
10331               if ( i == iEnd ) i = iBeg + iDelta;
10332               if ( i == i1 ) break;
10333               nodes.push_back ( f->GetNode( i ) );
10334             }
10335           }
10336         }
10337       }
10338     }
10339     // check similarity of elements of the sides
10340     if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10341       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10342       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10343         return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10344       }
10345       else {
10346         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10347       }
10348     }
10349
10350     // set nodes to merge
10351     // -------------------
10352
10353     if ( face[0] && face[1] )  {
10354       if ( nbNodes[0] != nbNodes[1] ) {
10355         MESSAGE("Diff nb of face nodes");
10356         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10357       }
10358 #ifdef DEBUG_MATCHING_NODES
10359       MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10360                 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10361                 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10362 #endif
10363       int nbN = nbNodes[0];
10364       {
10365         list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10366         list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10367         for ( int i = 0 ; i < nbN - 2; ++i ) {
10368 #ifdef DEBUG_MATCHING_NODES
10369           MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10370 #endif
10371           nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10372         }
10373       }
10374
10375       // add other links of the face 1 to linkList
10376       // -----------------------------------------
10377
10378       const SMDS_MeshElement* f0 = face[0];
10379       const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10380       for ( int i = 0; i < nbN; i++ )
10381       {
10382         const SMDS_MeshNode* n2 = f0->GetNode( i );
10383         pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10384           linkSet.insert( SMESH_TLink( n1, n2 ));
10385         if ( !iter_isnew.second ) { // already in a set: no need to process
10386           linkSet.erase( iter_isnew.first );
10387         }
10388         else // new in set == encountered for the first time: add
10389         {
10390 #ifdef DEBUG_MATCHING_NODES
10391           MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10392                     << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10393 #endif
10394           linkList[0].push_back ( NLink( n1, n2 ));
10395           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10396         }
10397         n1 = n2;
10398       }
10399     } // 2 faces found
10400   } // loop on link lists
10401
10402   return SEW_OK;
10403 }
10404
10405 namespace // automatically find theAffectedElems for DoubleNodes()
10406 {
10407   bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem );
10408
10409   //--------------------------------------------------------------------------------
10410   // Nodes shared by adjacent FissureBorder's.
10411   // 1 node  if FissureBorder separates faces
10412   // 2 nodes if FissureBorder separates volumes
10413   struct SubBorder
10414   {
10415     const SMDS_MeshNode* _nodes[2];
10416     int                  _nbNodes;
10417
10418     SubBorder( const SMDS_MeshNode* n1, const SMDS_MeshNode* n2 = 0 )
10419     {
10420       _nodes[0] = n1;
10421       _nodes[1] = n2;
10422       _nbNodes = bool( n1 ) + bool( n2 );
10423       if ( _nbNodes == 2 && n1 > n2 )
10424         std::swap( _nodes[0], _nodes[1] );
10425     }
10426     bool operator<( const SubBorder& other ) const
10427     {
10428       for ( int i = 0; i < _nbNodes; ++i )
10429       {
10430         if ( _nodes[i] < other._nodes[i] ) return true;
10431         if ( _nodes[i] > other._nodes[i] ) return false;
10432       }
10433       return false;
10434     }
10435   };
10436
10437   //--------------------------------------------------------------------------------
10438   // Map a SubBorder to all FissureBorder it bounds
10439   struct FissureBorder;
10440   typedef std::map< SubBorder, std::vector< FissureBorder* > > TBorderLinks;
10441   typedef TBorderLinks::iterator                               TMappedSub;
10442
10443   //--------------------------------------------------------------------------------
10444   /*!
10445    * \brief Element border (volume facet or face edge) at a fissure
10446    */
10447   struct FissureBorder
10448   {
10449     std::vector< const SMDS_MeshNode* > _nodes;    // border nodes
10450     const SMDS_MeshElement*             _elems[2]; // volume or face adjacent to fissure
10451
10452     std::vector< TMappedSub           > _mappedSubs;  // Sub() in TBorderLinks map
10453     std::vector< const SMDS_MeshNode* > _sortedNodes; // to compare FissureBorder's
10454
10455     FissureBorder( FissureBorder && from ) // move constructor
10456     {
10457       std::swap( _nodes,       from._nodes );
10458       std::swap( _sortedNodes, from._sortedNodes );
10459       _elems[0] = from._elems[0];
10460       _elems[1] = from._elems[1];
10461     }
10462
10463     FissureBorder( const SMDS_MeshElement*                  elemToDuplicate,
10464                    std::vector< const SMDS_MeshElement* > & adjElems)
10465       : _nodes( elemToDuplicate->NbCornerNodes() )
10466     {
10467       for ( size_t i = 0; i < _nodes.size(); ++i )
10468         _nodes[i] = elemToDuplicate->GetNode( i );
10469
10470       SMDSAbs_ElementType type = SMDSAbs_ElementType( elemToDuplicate->GetType() + 1 );
10471       findAdjacent( type, adjElems );
10472     }
10473
10474     FissureBorder( const SMDS_MeshNode**                    nodes,
10475                    const size_t                             nbNodes,
10476                    const SMDSAbs_ElementType                adjElemsType,
10477                    std::vector< const SMDS_MeshElement* > & adjElems)
10478       : _nodes( nodes, nodes + nbNodes )
10479     {
10480       findAdjacent( adjElemsType, adjElems );
10481     }
10482
10483     void findAdjacent( const SMDSAbs_ElementType                adjElemsType,
10484                        std::vector< const SMDS_MeshElement* > & adjElems)
10485     {
10486       _elems[0] = _elems[1] = 0;
10487       adjElems.clear();
10488       if ( SMDS_Mesh::GetElementsByNodes( _nodes, adjElems, adjElemsType ))
10489         for ( size_t i = 0; i < adjElems.size() && i < 2; ++i )
10490           _elems[i] = adjElems[i];
10491     }
10492
10493     bool operator<( const FissureBorder& other ) const
10494     {
10495       return GetSortedNodes() < other.GetSortedNodes();
10496     }
10497
10498     const std::vector< const SMDS_MeshNode* >& GetSortedNodes() const
10499     {
10500       if ( _sortedNodes.empty() && !_nodes.empty() )
10501       {
10502         FissureBorder* me = const_cast<FissureBorder*>( this );
10503         me->_sortedNodes = me->_nodes;
10504         std::sort( me->_sortedNodes.begin(), me->_sortedNodes.end() );
10505       }
10506       return _sortedNodes;
10507     }
10508
10509     size_t NbSub() const
10510     {
10511       return _nodes.size();
10512     }
10513
10514     SubBorder Sub(size_t i) const
10515     {
10516       return SubBorder( _nodes[i], NbSub() > 2 ? _nodes[ (i+1)%NbSub() ] : 0 );
10517     }
10518
10519     void AddSelfTo( TBorderLinks& borderLinks )
10520     {
10521       _mappedSubs.resize( NbSub() );
10522       for ( size_t i = 0; i < NbSub(); ++i )
10523       {
10524         TBorderLinks::iterator s2b =
10525           borderLinks.insert( std::make_pair( Sub(i), TBorderLinks::mapped_type() )).first;
10526         s2b->second.push_back( this );
10527         _mappedSubs[ i ] = s2b;
10528       }
10529     }
10530
10531     void Clear()
10532     {
10533       _nodes.clear();
10534     }
10535
10536     const SMDS_MeshElement* GetMarkedElem() const
10537     {
10538       if ( _nodes.empty() ) return 0; // cleared
10539       if ( _elems[0] && _elems[0]->isMarked() ) return _elems[0];
10540       if ( _elems[1] && _elems[1]->isMarked() ) return _elems[1];
10541       return 0;
10542     }
10543
10544     gp_XYZ GetNorm() const // normal to the border
10545     {
10546       gp_XYZ norm;
10547       if ( _nodes.size() == 2 )
10548       {
10549         gp_XYZ avgNorm( 0,0,0 ); // sum of normals of adjacent faces
10550         if ( SMESH_MeshAlgos::FaceNormal( _elems[0], norm ))
10551           avgNorm += norm;
10552         if ( SMESH_MeshAlgos::FaceNormal( _elems[1], norm ))
10553           avgNorm += norm;
10554
10555         gp_XYZ bordDir( SMESH_NodeXYZ( _nodes[0] ) - SMESH_NodeXYZ( _nodes[1] ));
10556         norm = bordDir ^ avgNorm;
10557       }
10558       else
10559       {
10560         SMESH_NodeXYZ p0( _nodes[0] );
10561         SMESH_NodeXYZ p1( _nodes[1] );
10562         SMESH_NodeXYZ p2( _nodes[2] );
10563         norm = ( p0 - p1 ) ^ ( p2 - p1 );
10564       }
10565       if ( isOut( _nodes[0], norm, GetMarkedElem() ))
10566         norm.Reverse();
10567
10568       return norm;
10569     }
10570
10571     void ChooseSide() // mark an _elem located at positive side of fissure
10572     {
10573       _elems[0]->setIsMarked( true );
10574       gp_XYZ norm = GetNorm();
10575       double maxX = norm.Coord(1);
10576       if ( Abs( maxX ) < Abs( norm.Coord(2)) ) maxX = norm.Coord(2);
10577       if ( Abs( maxX ) < Abs( norm.Coord(3)) ) maxX = norm.Coord(3);
10578       if ( maxX < 0 )
10579       {
10580         _elems[0]->setIsMarked( false );
10581         _elems[1]->setIsMarked( true );
10582       }
10583     }
10584
10585   }; // struct FissureBorder
10586
10587   //--------------------------------------------------------------------------------
10588   /*!
10589    * \brief Classifier of elements at fissure edge
10590    */
10591   class FissureNormal
10592   {
10593     std::vector< gp_XYZ > _normals;
10594     bool                  _bothIn;
10595
10596   public:
10597     void Add( const SMDS_MeshNode* n, const FissureBorder& bord )
10598     {
10599       _bothIn = false;
10600       _normals.reserve(2);
10601       _normals.push_back( bord.GetNorm() );
10602       if ( _normals.size() == 2 )
10603         _bothIn = !isOut( n, _normals[0], bord.GetMarkedElem() );
10604     }
10605
10606     bool IsIn( const SMDS_MeshNode* n, const SMDS_MeshElement* elem ) const
10607     {
10608       bool isIn = false;
10609       switch ( _normals.size() ) {
10610       case 1:
10611       {
10612         isIn = !isOut( n, _normals[0], elem );
10613         break;
10614       }
10615       case 2:
10616       {
10617         bool in1 = !isOut( n, _normals[0], elem );
10618         bool in2 = !isOut( n, _normals[1], elem );
10619         isIn = _bothIn ? ( in1 && in2 ) : ( in1 || in2 );
10620       }
10621       }
10622       return isIn;
10623     }
10624   };
10625
10626   //================================================================================
10627   /*!
10628    * \brief Classify an element by a plane passing through a node
10629    */
10630   //================================================================================
10631
10632   bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem )
10633   {
10634     SMESH_NodeXYZ p = n;
10635     double sumDot = 0;
10636     for ( int i = 0, nb = elem->NbCornerNodes(); i < nb; ++i )
10637     {
10638       SMESH_NodeXYZ pi = elem->GetNode( i );
10639       sumDot += norm * ( pi - p );
10640     }
10641     return sumDot < -1e-100;
10642   }
10643
10644   //================================================================================
10645   /*!
10646    * \brief Find FissureBorder's by nodes to duplicate
10647    */
10648   //================================================================================
10649
10650   void findFissureBorders( const TIDSortedElemSet&        theNodes,
10651                            std::vector< FissureBorder > & theFissureBorders )
10652   {
10653     TIDSortedElemSet::const_iterator nIt = theNodes.begin();
10654     const SMDS_MeshNode* n = dynamic_cast< const SMDS_MeshNode*>( *nIt );
10655     if ( !n ) return;
10656     SMDSAbs_ElementType elemType = SMDSAbs_Volume;
10657     if ( n->NbInverseElements( elemType ) == 0 )
10658     {
10659       elemType = SMDSAbs_Face;
10660       if ( n->NbInverseElements( elemType ) == 0 )
10661         return;
10662     }
10663     // unmark elements touching the fissure
10664     for ( ; nIt != theNodes.end(); ++nIt )
10665       SMESH_MeshAlgos::MarkElems( cast2Node(*nIt)->GetInverseElementIterator(), false );
10666
10667     // loop on elements touching the fissure to get their borders belonging to the fissure
10668     std::set< FissureBorder >              fissureBorders;
10669     std::vector< const SMDS_MeshElement* > adjElems;
10670     std::vector< const SMDS_MeshNode* >    nodes;
10671     SMDS_VolumeTool volTool;
10672     for ( nIt = theNodes.begin(); nIt != theNodes.end(); ++nIt )
10673     {
10674       SMDS_ElemIteratorPtr invIt = cast2Node(*nIt)->GetInverseElementIterator( elemType );
10675       while ( invIt->more() )
10676       {
10677         const SMDS_MeshElement* eInv = invIt->next();
10678         if ( eInv->isMarked() ) continue;
10679         eInv->setIsMarked( true );
10680
10681         if ( elemType == SMDSAbs_Volume )
10682         {
10683           volTool.Set( eInv );
10684           int iQuad = eInv->IsQuadratic() ? 2 : 1;
10685           for ( int iF = 0, nbF = volTool.NbFaces(); iF < nbF; ++iF )
10686           {
10687             const SMDS_MeshNode** nn = volTool.GetFaceNodes( iF );
10688             int                  nbN = volTool.NbFaceNodes( iF ) / iQuad;
10689             nodes.clear();
10690             bool allOnFissure = true;
10691             for ( int iN = 0; iN < nbN  && allOnFissure; iN += iQuad )
10692               if (( allOnFissure = theNodes.count( nn[ iN ])))
10693                 nodes.push_back( nn[ iN ]);
10694             if ( allOnFissure )
10695               fissureBorders.insert( std::move( FissureBorder( &nodes[0], nodes.size(),
10696                                                                elemType, adjElems )));
10697           }
10698         }
10699         else // elemType == SMDSAbs_Face
10700         {
10701           const SMDS_MeshNode* nn[2] = { eInv->GetNode( eInv->NbCornerNodes()-1 ), 0 };
10702           bool            onFissure0 = theNodes.count( nn[0] ), onFissure1;
10703           for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN; ++iN )
10704           {
10705             nn[1]      = eInv->GetNode( iN );
10706             onFissure1 = theNodes.count( nn[1] );
10707             if ( onFissure0 && onFissure1 )
10708               fissureBorders.insert( std::move( FissureBorder( nn, 2, elemType, adjElems )));
10709             nn[0]      = nn[1];
10710             onFissure0 = onFissure1;
10711           }
10712         }
10713       }
10714     }
10715
10716     theFissureBorders.reserve( theFissureBorders.size() + fissureBorders.size());
10717     std::set< FissureBorder >::iterator bord = fissureBorders.begin();
10718     for ( ; bord != fissureBorders.end(); ++bord )
10719     {
10720       theFissureBorders.push_back( std::move( const_cast<FissureBorder&>( *bord ) ));
10721     }
10722     return;
10723   } // findFissureBorders()
10724
10725   //================================================================================
10726   /*!
10727    * \brief Find elements on one side of a fissure defined by elements or nodes to duplicate
10728    *  \param [in] theElemsOrNodes - elements or nodes to duplicate
10729    *  \param [in] theNodesNot - nodes not to duplicate
10730    *  \param [out] theAffectedElems - the found elements
10731    */
10732   //================================================================================
10733
10734   void findAffectedElems( const TIDSortedElemSet& theElemsOrNodes,
10735                           TIDSortedElemSet&       theAffectedElems)
10736   {
10737     if ( theElemsOrNodes.empty() ) return;
10738
10739     // find FissureBorder's
10740
10741     std::vector< FissureBorder >           fissure;
10742     std::vector< const SMDS_MeshElement* > elemsByFacet;
10743
10744     TIDSortedElemSet::const_iterator elIt = theElemsOrNodes.begin();
10745     if ( (*elIt)->GetType() == SMDSAbs_Node )
10746     {
10747       findFissureBorders( theElemsOrNodes, fissure );
10748     }
10749     else
10750     {
10751       fissure.reserve( theElemsOrNodes.size() );
10752       for ( ; elIt != theElemsOrNodes.end(); ++elIt )
10753         fissure.push_back( std::move( FissureBorder( *elIt, elemsByFacet )));
10754     }
10755     if ( fissure.empty() )
10756       return;
10757
10758     // fill borderLinks
10759
10760     TBorderLinks borderLinks;
10761
10762     for ( size_t i = 0; i < fissure.size(); ++i )
10763     {
10764       fissure[i].AddSelfTo( borderLinks );
10765     }
10766
10767     // get theAffectedElems
10768
10769     // unmark elements having nodes on the fissure, theAffectedElems elements will be marked
10770     for ( size_t i = 0; i < fissure.size(); ++i )
10771       for ( size_t j = 0; j < fissure[i]._nodes.size(); ++j )
10772       {
10773         SMESH_MeshAlgos::MarkElemNodes( fissure[i]._nodes[j]->GetInverseElementIterator(),
10774                                         false, /*markElem=*/true );
10775       }
10776
10777     std::vector<const SMDS_MeshNode *>                 facetNodes;
10778     std::map< const SMDS_MeshNode*, FissureNormal >    fissEdgeNodes2Norm;
10779     boost::container::flat_set< const SMDS_MeshNode* > fissureNodes;
10780
10781     // choose a side of fissure
10782     fissure[0].ChooseSide();
10783     theAffectedElems.insert( fissure[0].GetMarkedElem() );
10784
10785     size_t nbCheckedBorders = 0;
10786     while ( nbCheckedBorders < fissure.size() )
10787     {
10788       // find a FissureBorder to treat
10789       FissureBorder* bord = 0;
10790       for ( size_t i = 0; i < fissure.size()  && !bord; ++i )
10791         if ( fissure[i].GetMarkedElem() )
10792           bord = & fissure[i];
10793       for ( size_t i = 0; i < fissure.size()  && !bord; ++i )
10794         if ( fissure[i].NbSub() > 0 && fissure[i]._elems[0] )
10795         {
10796           bord = & fissure[i];
10797           bord->ChooseSide();
10798           theAffectedElems.insert( bord->GetMarkedElem() );
10799         }
10800       if ( !bord ) return;
10801       ++nbCheckedBorders;
10802
10803       // treat FissureBorder's linked to bord
10804       fissureNodes.clear();
10805       fissureNodes.insert( bord->_nodes.begin(), bord->_nodes.end() );
10806       for ( size_t i = 0; i < bord->NbSub(); ++i )
10807       {
10808         TBorderLinks::iterator l2b = bord->_mappedSubs[ i ];
10809         if ( l2b == borderLinks.end() || l2b->second.empty() ) continue;
10810         std::vector< FissureBorder* >& linkedBorders = l2b->second;
10811         const SubBorder&                          sb = l2b->first;
10812         const SMDS_MeshElement*             bordElem = bord->GetMarkedElem();
10813
10814         if ( linkedBorders.size() == 1 ) // fissure edge reached, fill fissEdgeNodes2Norm
10815         {
10816           for ( int j = 0; j < sb._nbNodes; ++j )
10817             fissEdgeNodes2Norm[ sb._nodes[j] ].Add( sb._nodes[j], *bord );
10818           continue;
10819         }
10820
10821         // add to theAffectedElems elems sharing nodes of a SubBorder and a node of bordElem
10822         // until an elem adjacent to a neighbour FissureBorder is found
10823         facetNodes.clear();
10824         facetNodes.insert( facetNodes.end(), sb._nodes, sb._nodes + sb._nbNodes );
10825         facetNodes.resize( sb._nbNodes + 1 );
10826
10827         while ( bordElem )
10828         {
10829           // check if bordElem is adjacent to a neighbour FissureBorder
10830           for ( size_t j = 0; j < linkedBorders.size(); ++j )
10831           {
10832             FissureBorder* bord2 = linkedBorders[j];
10833             if ( bord2 == bord ) continue;
10834             if ( bordElem == bord2->_elems[0] || bordElem == bord2->_elems[1] )
10835               bordElem = 0;
10836             else
10837               fissureNodes.insert( bord2->_nodes.begin(), bord2->_nodes.end() );
10838           }
10839           if ( !bordElem )
10840             break;
10841
10842           // find the next bordElem
10843           const SMDS_MeshElement* nextBordElem = 0;
10844           for ( int iN = 0, nbN = bordElem->NbCornerNodes(); iN < nbN  && !nextBordElem; ++iN )
10845           {
10846             const SMDS_MeshNode* n = bordElem->GetNode( iN );
10847             if ( fissureNodes.count( n )) continue;
10848
10849             facetNodes[ sb._nbNodes ] = n;
10850             elemsByFacet.clear();
10851             if ( SMDS_Mesh::GetElementsByNodes( facetNodes, elemsByFacet ) > 1 )
10852             {
10853               for ( size_t iE = 0; iE < elemsByFacet.size(); ++iE )
10854                 if ( elemsByFacet[ iE ] != bordElem &&
10855                      !elemsByFacet[ iE ]->isMarked() )
10856                 {
10857                   theAffectedElems.insert( elemsByFacet[ iE ]);
10858                   elemsByFacet[ iE ]->setIsMarked( true );
10859                   if ( elemsByFacet[ iE ]->GetType() == bordElem->GetType() )
10860                     nextBordElem = elemsByFacet[ iE ];
10861                 }
10862             }
10863           }
10864           bordElem = nextBordElem;
10865
10866         } // while ( bordElem )
10867
10868         linkedBorders.clear(); // not to treat this link any more
10869
10870       } // loop on SubBorder's of a FissureBorder
10871
10872       bord->Clear();
10873
10874     } // loop on FissureBorder's
10875
10876
10877     // add elements sharing only one node of the fissure, except those sharing fissure edge nodes
10878
10879     // mark nodes of theAffectedElems
10880     SMESH_MeshAlgos::MarkElemNodes( theAffectedElems.begin(), theAffectedElems.end(), true );
10881
10882     // unmark nodes of the fissure
10883     elIt = theElemsOrNodes.begin();
10884     if ( (*elIt)->GetType() == SMDSAbs_Node )
10885       SMESH_MeshAlgos::MarkElems( elIt, theElemsOrNodes.end(), false );
10886     else
10887       SMESH_MeshAlgos::MarkElemNodes( elIt, theElemsOrNodes.end(), false );
10888
10889     std::vector< gp_XYZ > normVec;
10890
10891     // loop on nodes of the fissure, add elements having marked nodes
10892     for ( elIt = theElemsOrNodes.begin(); elIt != theElemsOrNodes.end(); ++elIt )
10893     {
10894       const SMDS_MeshElement* e = (*elIt);
10895       if ( e->GetType() != SMDSAbs_Node )
10896         e->setIsMarked( true ); // avoid adding a fissure element
10897
10898       for ( int iN = 0, nbN = e->NbCornerNodes(); iN < nbN; ++iN )
10899       {
10900         const SMDS_MeshNode* n = e->GetNode( iN );
10901         if ( fissEdgeNodes2Norm.count( n ))
10902           continue;
10903
10904         SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
10905         while ( invIt->more() )
10906         {
10907           const SMDS_MeshElement* eInv = invIt->next();
10908           if ( eInv->isMarked() ) continue;
10909           eInv->setIsMarked( true );
10910
10911           SMDS_ElemIteratorPtr nIt = eInv->nodesIterator();
10912           while( nIt->more() )
10913             if ( nIt->next()->isMarked())
10914             {
10915               theAffectedElems.insert( eInv );
10916               SMESH_MeshAlgos::MarkElems( eInv->nodesIterator(), true );
10917               n->setIsMarked( false );
10918               break;
10919             }
10920         }
10921       }
10922     }
10923
10924     // add elements on the fissure edge
10925     std::map< const SMDS_MeshNode*, FissureNormal >::iterator n2N;
10926     for ( n2N = fissEdgeNodes2Norm.begin(); n2N != fissEdgeNodes2Norm.end(); ++n2N )
10927     {
10928       const SMDS_MeshNode* edgeNode = n2N->first;
10929       const FissureNormal & normals = n2N->second;
10930
10931       SMDS_ElemIteratorPtr invIt = edgeNode->GetInverseElementIterator();
10932       while ( invIt->more() )
10933       {
10934         const SMDS_MeshElement* eInv = invIt->next();
10935         if ( eInv->isMarked() ) continue;
10936         eInv->setIsMarked( true );
10937
10938         // classify eInv using normals
10939         bool toAdd = normals.IsIn( edgeNode, eInv );
10940         if ( toAdd ) // check if all nodes lie on the fissure edge
10941         {
10942           bool notOnEdge = false;
10943           for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN  && !notOnEdge; ++iN )
10944             notOnEdge = !fissEdgeNodes2Norm.count( eInv->GetNode( iN ));
10945           toAdd = notOnEdge;
10946         }
10947         if ( toAdd )
10948         {
10949           theAffectedElems.insert( eInv );
10950         }
10951       }
10952     }
10953
10954     return;
10955   } // findAffectedElems()
10956 } // namespace
10957
10958 //================================================================================
10959 /*!
10960  * \brief Create elements equal (on same nodes) to given ones
10961  *  \param [in] theElements - a set of elems to duplicate. If it is empty, all
10962  *              elements of the uppest dimension are duplicated.
10963  */
10964 //================================================================================
10965
10966 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
10967 {
10968   ClearLastCreated();
10969   SMESHDS_Mesh* mesh = GetMeshDS();
10970
10971   // get an element type and an iterator over elements
10972
10973   SMDSAbs_ElementType type = SMDSAbs_All;
10974   SMDS_ElemIteratorPtr elemIt;
10975   if ( theElements.empty() )
10976   {
10977     if ( mesh->NbNodes() == 0 )
10978       return;
10979     // get most complex type
10980     SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10981       SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10982       SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10983     };
10984     for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10985       if ( mesh->GetMeshInfo().NbElements( types[i] ))
10986       {
10987         type = types[i];
10988         break;
10989       }
10990     elemIt = mesh->elementsIterator( type );
10991   }
10992   else
10993   {
10994     type = (*theElements.begin())->GetType();
10995     elemIt = SMESHUtils::elemSetIterator( theElements );
10996   }
10997
10998   // un-mark all elements to avoid duplicating just created elements
10999   SMESH_MeshAlgos::MarkElems( mesh->elementsIterator( type ), false );
11000
11001   // duplicate elements
11002
11003   ElemFeatures elemType;
11004
11005   vector< const SMDS_MeshNode* > nodes;
11006   while ( elemIt->more() )
11007   {
11008     const SMDS_MeshElement* elem = elemIt->next();
11009     if ( elem->GetType() != type || elem->isMarked() )
11010       continue;
11011
11012     elemType.Init( elem, /*basicOnly=*/false );
11013     nodes.assign( elem->begin_nodes(), elem->end_nodes() );
11014
11015     if ( const SMDS_MeshElement* newElem = AddElement( nodes, elemType ))
11016       newElem->setIsMarked( true );
11017   }
11018 }
11019
11020 //================================================================================
11021 /*!
11022   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11023   \param theElems - the list of elements (edges or faces) to be replicated
11024   The nodes for duplication could be found from these elements
11025   \param theNodesNot - list of nodes to NOT replicate
11026   \param theAffectedElems - the list of elements (cells and edges) to which the
11027   replicated nodes should be associated to.
11028   \return TRUE if operation has been completed successfully, FALSE otherwise
11029 */
11030 //================================================================================
11031
11032 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
11033                                     const TIDSortedElemSet& theNodesNot,
11034                                     const TIDSortedElemSet& theAffectedElems )
11035 {
11036   ClearLastCreated();
11037
11038   if ( theElems.size() == 0 )
11039     return false;
11040
11041   SMESHDS_Mesh* aMeshDS = GetMeshDS();
11042   if ( !aMeshDS )
11043     return false;
11044
11045   bool res = false;
11046   TNodeNodeMap anOldNodeToNewNode;
11047   // duplicate elements and nodes
11048   res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
11049   // replce nodes by duplications
11050   res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
11051   return res;
11052 }
11053
11054 //================================================================================
11055 /*!
11056   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11057   \param theMeshDS - mesh instance
11058   \param theElems - the elements replicated or modified (nodes should be changed)
11059   \param theNodesNot - nodes to NOT replicate
11060   \param theNodeNodeMap - relation of old node to new created node
11061   \param theIsDoubleElem - flag os to replicate element or modify
11062   \return TRUE if operation has been completed successfully, FALSE otherwise
11063 */
11064 //================================================================================
11065
11066 bool SMESH_MeshEditor::doubleNodes(SMESHDS_Mesh*           theMeshDS,
11067                                    const TIDSortedElemSet& theElems,
11068                                    const TIDSortedElemSet& theNodesNot,
11069                                    TNodeNodeMap&           theNodeNodeMap,
11070                                    const bool              theIsDoubleElem )
11071 {
11072   // iterate through element and duplicate them (by nodes duplication)
11073   bool res = false;
11074   std::vector<const SMDS_MeshNode*> newNodes;
11075   ElemFeatures elemType;
11076
11077   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11078   for ( ;  elemItr != theElems.end(); ++elemItr )
11079   {
11080     const SMDS_MeshElement* anElem = *elemItr;
11081     // if (!anElem)
11082     //   continue;
11083
11084     // duplicate nodes to duplicate element
11085     bool isDuplicate = false;
11086     newNodes.resize( anElem->NbNodes() );
11087     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
11088     int ind = 0;
11089     while ( anIter->more() )
11090     {
11091       const SMDS_MeshNode* aCurrNode = static_cast<const SMDS_MeshNode*>( anIter->next() );
11092       const SMDS_MeshNode*  aNewNode = aCurrNode;
11093       TNodeNodeMap::iterator     n2n = theNodeNodeMap.find( aCurrNode );
11094       if ( n2n != theNodeNodeMap.end() )
11095       {
11096         aNewNode = n2n->second;
11097       }
11098       else if ( theIsDoubleElem && !theNodesNot.count( aCurrNode ))
11099       {
11100         // duplicate node
11101         aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
11102         copyPosition( aCurrNode, aNewNode );
11103         theNodeNodeMap[ aCurrNode ] = aNewNode;
11104         myLastCreatedNodes.push_back( aNewNode );
11105       }
11106       isDuplicate |= (aCurrNode != aNewNode);
11107       newNodes[ ind++ ] = aNewNode;
11108     }
11109     if ( !isDuplicate )
11110       continue;
11111
11112     if ( theIsDoubleElem )
11113       AddElement( newNodes, elemType.Init( anElem, /*basicOnly=*/false ));
11114     else
11115       theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], newNodes.size() );
11116
11117     res = true;
11118   }
11119   return res;
11120 }
11121
11122 //================================================================================
11123 /*!
11124   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11125   \param theNodes - identifiers of nodes to be doubled
11126   \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
11127   nodes. If list of element identifiers is empty then nodes are doubled but
11128   they not assigned to elements
11129   \return TRUE if operation has been completed successfully, FALSE otherwise
11130 */
11131 //================================================================================
11132
11133 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
11134                                     const std::list< int >& theListOfModifiedElems )
11135 {
11136   ClearLastCreated();
11137
11138   if ( theListOfNodes.size() == 0 )
11139     return false;
11140
11141   SMESHDS_Mesh* aMeshDS = GetMeshDS();
11142   if ( !aMeshDS )
11143     return false;
11144
11145   // iterate through nodes and duplicate them
11146
11147   std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
11148
11149   std::list< int >::const_iterator aNodeIter;
11150   for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
11151   {
11152     const SMDS_MeshNode* aNode = aMeshDS->FindNode( *aNodeIter );
11153     if ( !aNode )
11154       continue;
11155
11156     // duplicate node
11157
11158     const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
11159     if ( aNewNode )
11160     {
11161       copyPosition( aNode, aNewNode );
11162       anOldNodeToNewNode[ aNode ] = aNewNode;
11163       myLastCreatedNodes.push_back( aNewNode );
11164     }
11165   }
11166
11167   // Change nodes of elements
11168
11169   std::vector<const SMDS_MeshNode*> aNodeArr;
11170
11171   std::list< int >::const_iterator anElemIter;
11172   for ( anElemIter =  theListOfModifiedElems.begin();
11173         anElemIter != theListOfModifiedElems.end();
11174         anElemIter++ )
11175   {
11176     const SMDS_MeshElement* anElem = aMeshDS->FindElement( *anElemIter );
11177     if ( !anElem )
11178       continue;
11179
11180     aNodeArr.assign( anElem->begin_nodes(), anElem->end_nodes() );
11181     for( size_t i = 0; i < aNodeArr.size(); ++i )
11182     {
11183       std::map< const SMDS_MeshNode*, const SMDS_MeshNode* >::iterator n2n =
11184         anOldNodeToNewNode.find( aNodeArr[ i ]);
11185       if ( n2n != anOldNodeToNewNode.end() )
11186         aNodeArr[ i ] = n2n->second;
11187     }
11188     aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], aNodeArr.size() );
11189   }
11190
11191   return true;
11192 }
11193
11194 namespace {
11195
11196   //================================================================================
11197   /*!
11198     \brief Check if element located inside shape
11199     \return TRUE if IN or ON shape, FALSE otherwise
11200   */
11201   //================================================================================
11202
11203   template<class Classifier>
11204   bool isInside(const SMDS_MeshElement* theElem,
11205                 Classifier&             theClassifier,
11206                 const double            theTol)
11207   {
11208     gp_XYZ centerXYZ (0, 0, 0);
11209     SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
11210     while ( aNodeItr->more() )
11211       centerXYZ += SMESH_NodeXYZ( aNodeItr->next() );
11212
11213     gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
11214     theClassifier.Perform(aPnt, theTol);
11215     TopAbs_State aState = theClassifier.State();
11216     return (aState == TopAbs_IN || aState == TopAbs_ON );
11217   }
11218
11219   //================================================================================
11220   /*!
11221    * \brief Classifier of the 3D point on the TopoDS_Face
11222    *        with interaface suitable for isInside()
11223    */
11224   //================================================================================
11225
11226   struct _FaceClassifier
11227   {
11228     Extrema_ExtPS       _extremum;
11229     BRepAdaptor_Surface _surface;
11230     TopAbs_State        _state;
11231
11232     _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
11233     {
11234       _extremum.Initialize( _surface,
11235                             _surface.FirstUParameter(), _surface.LastUParameter(),
11236                             _surface.FirstVParameter(), _surface.LastVParameter(),
11237                             _surface.Tolerance(), _surface.Tolerance() );
11238     }
11239     void Perform(const gp_Pnt& aPnt, double theTol)
11240     {
11241       theTol *= theTol;
11242       _state = TopAbs_OUT;
11243       _extremum.Perform(aPnt);
11244       if ( _extremum.IsDone() )
11245         for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
11246           _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
11247     }
11248     TopAbs_State State() const
11249     {
11250       return _state;
11251     }
11252   };
11253 }
11254
11255 //================================================================================
11256 /*!
11257   \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
11258   This method is the first step of DoubleNodeElemGroupsInRegion.
11259   \param theElems - list of groups of elements (edges or faces) to be replicated
11260   \param theNodesNot - list of groups of nodes not to replicated
11261   \param theShape - shape to detect affected elements (element which geometric center
11262          located on or inside shape). If the shape is null, detection is done on faces orientations
11263          (select elements with a gravity center on the side given by faces normals).
11264          This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
11265          The replicated nodes should be associated to affected elements.
11266   \return true
11267   \sa DoubleNodeElemGroupsInRegion()
11268 */
11269 //================================================================================
11270
11271 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
11272                                                    const TIDSortedElemSet& theNodesNot,
11273                                                    const TopoDS_Shape&     theShape,
11274                                                    TIDSortedElemSet&       theAffectedElems)
11275 {
11276   if ( theShape.IsNull() )
11277   {
11278     findAffectedElems( theElems, theAffectedElems );
11279   }
11280   else
11281   {
11282     const double aTol = Precision::Confusion();
11283     auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
11284     auto_ptr<_FaceClassifier>              aFaceClassifier;
11285     if ( theShape.ShapeType() == TopAbs_SOLID )
11286     {
11287       bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
11288       bsc3d->PerformInfinitePoint(aTol);
11289     }
11290     else if (theShape.ShapeType() == TopAbs_FACE )
11291     {
11292       aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
11293     }
11294
11295     // iterates on indicated elements and get elements by back references from their nodes
11296     TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11297     for ( ;  elemItr != theElems.end(); ++elemItr )
11298     {
11299       SMDS_MeshElement*     anElem = (SMDS_MeshElement*)*elemItr;
11300       SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11301       while ( nodeItr->more() )
11302       {
11303         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11304         if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11305           continue;
11306         SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11307         while ( backElemItr->more() )
11308         {
11309           const SMDS_MeshElement* curElem = backElemItr->next();
11310           if ( curElem && theElems.find(curElem) == theElems.end() &&
11311                ( bsc3d.get() ?
11312                  isInside( curElem, *bsc3d, aTol ) :
11313                  isInside( curElem, *aFaceClassifier, aTol )))
11314             theAffectedElems.insert( curElem );
11315         }
11316       }
11317     }
11318   }
11319   return true;
11320 }
11321
11322 //================================================================================
11323 /*!
11324   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11325   \param theElems - group of of elements (edges or faces) to be replicated
11326   \param theNodesNot - group of nodes not to replicate
11327   \param theShape - shape to detect affected elements (element which geometric center
11328   located on or inside shape).
11329   The replicated nodes should be associated to affected elements.
11330   \return TRUE if operation has been completed successfully, FALSE otherwise
11331 */
11332 //================================================================================
11333
11334 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
11335                                             const TIDSortedElemSet& theNodesNot,
11336                                             const TopoDS_Shape&     theShape )
11337 {
11338   if ( theShape.IsNull() )
11339     return false;
11340
11341   const double aTol = Precision::Confusion();
11342   SMESHUtils::Deleter< BRepClass3d_SolidClassifier> bsc3d;
11343   SMESHUtils::Deleter<_FaceClassifier>              aFaceClassifier;
11344   if ( theShape.ShapeType() == TopAbs_SOLID )
11345   {
11346     bsc3d._obj = new BRepClass3d_SolidClassifier( theShape );
11347     bsc3d->PerformInfinitePoint(aTol);
11348   }
11349   else if (theShape.ShapeType() == TopAbs_FACE )
11350   {
11351     aFaceClassifier._obj = new _FaceClassifier( TopoDS::Face( theShape ));
11352   }
11353
11354   // iterates on indicated elements and get elements by back references from their nodes
11355   TIDSortedElemSet anAffected;
11356   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11357   for ( ;  elemItr != theElems.end(); ++elemItr )
11358   {
11359     SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11360     if (!anElem)
11361       continue;
11362
11363     SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11364     while ( nodeItr->more() )
11365     {
11366       const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11367       if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11368         continue;
11369       SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11370       while ( backElemItr->more() )
11371       {
11372         const SMDS_MeshElement* curElem = backElemItr->next();
11373         if ( curElem && theElems.find(curElem) == theElems.end() &&
11374              ( bsc3d ?
11375                isInside( curElem, *bsc3d, aTol ) :
11376                isInside( curElem, *aFaceClassifier, aTol )))
11377           anAffected.insert( curElem );
11378       }
11379     }
11380   }
11381   return DoubleNodes( theElems, theNodesNot, anAffected );
11382 }
11383
11384 /*!
11385  *  \brief compute an oriented angle between two planes defined by four points.
11386  *  The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
11387  *  @param p0 base of the rotation axe
11388  *  @param p1 extremity of the rotation axe
11389  *  @param g1 belongs to the first plane
11390  *  @param g2 belongs to the second plane
11391  */
11392 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
11393 {
11394   gp_Vec vref(p0, p1);
11395   gp_Vec v1(p0, g1);
11396   gp_Vec v2(p0, g2);
11397   gp_Vec n1 = vref.Crossed(v1);
11398   gp_Vec n2 = vref.Crossed(v2);
11399   try {
11400     return n2.AngleWithRef(n1, vref);
11401   }
11402   catch ( Standard_Failure ) {
11403   }
11404   return Max( v1.Magnitude(), v2.Magnitude() );
11405 }
11406
11407 /*!
11408  * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11409  *  The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
11410  * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
11411  * Triangles are transformed into prisms, and quadrangles into hexahedrons.
11412  * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
11413  * 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.
11414  * 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.
11415  * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
11416  * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
11417  * \param theElems - list of groups of volumes, where a group of volume is a set of
11418  *        SMDS_MeshElements sorted by Id.
11419  * \param createJointElems - if TRUE, create the elements
11420  * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
11421  *        the boundary between \a theDomains and the rest mesh
11422  * \return TRUE if operation has been completed successfully, FALSE otherwise
11423  */
11424 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11425                                                      bool                                 createJointElems,
11426                                                      bool                                 onAllBoundaries)
11427 {
11428   // MESSAGE("----------------------------------------------");
11429   // MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11430   // MESSAGE("----------------------------------------------");
11431
11432   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11433   meshDS->BuildDownWardConnectivity(true);
11434   CHRONO(50);
11435   SMDS_UnstructuredGrid *grid = meshDS->getGrid();
11436
11437   // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11438   //     build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11439   //     build the list of nodes shared by 2 or more domains, with their domain indexes
11440
11441   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11442   std::map<int,int>celldom; // cell vtkId --> domain
11443   std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains;  // oldNode --> (id domain --> id cell)
11444   std::map<int, std::map<int,int> > nodeDomains; // oldId -->  (domainId --> newId)
11445   faceDomains.clear();
11446   celldom.clear();
11447   cellDomains.clear();
11448   nodeDomains.clear();
11449   std::map<int,int> emptyMap;
11450   std::set<int> emptySet;
11451   emptyMap.clear();
11452
11453   //MESSAGE(".. Number of domains :"<<theElems.size());
11454
11455   TIDSortedElemSet theRestDomElems;
11456   const int iRestDom  = -1;
11457   const int idom0     = onAllBoundaries ? iRestDom : 0;
11458   const int nbDomains = theElems.size();
11459
11460   // Check if the domains do not share an element
11461   for (int idom = 0; idom < nbDomains-1; idom++)
11462   {
11463     //       MESSAGE("... Check of domain #" << idom);
11464     const TIDSortedElemSet& domain = theElems[idom];
11465     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11466     for (; elemItr != domain.end(); ++elemItr)
11467     {
11468       const SMDS_MeshElement* anElem = *elemItr;
11469       int idombisdeb = idom + 1 ;
11470       // check if the element belongs to a domain further in the list
11471       for ( size_t idombis = idombisdeb; idombis < theElems.size(); idombis++ )
11472       {
11473         const TIDSortedElemSet& domainbis = theElems[idombis];
11474         if ( domainbis.count( anElem ))
11475         {
11476           MESSAGE(".... Domain #" << idom);
11477           MESSAGE(".... Domain #" << idombis);
11478           throw SALOME_Exception("The domains are not disjoint.");
11479           return false ;
11480         }
11481       }
11482     }
11483   }
11484
11485   for (int idom = 0; idom < nbDomains; idom++)
11486   {
11487
11488     // --- build a map (face to duplicate --> volume to modify)
11489     //     with all the faces shared by 2 domains (group of elements)
11490     //     and corresponding volume of this domain, for each shared face.
11491     //     a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11492
11493     //MESSAGE("... Neighbors of domain #" << idom);
11494     const TIDSortedElemSet& domain = theElems[idom];
11495     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11496     for (; elemItr != domain.end(); ++elemItr)
11497     {
11498       const SMDS_MeshElement* anElem = *elemItr;
11499       if (!anElem)
11500         continue;
11501       int vtkId = anElem->getVtkId();
11502       //MESSAGE("  vtkId " << vtkId << " smdsId " << anElem->GetID());
11503       int neighborsVtkIds[NBMAXNEIGHBORS];
11504       int downIds[NBMAXNEIGHBORS];
11505       unsigned char downTypes[NBMAXNEIGHBORS];
11506       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11507       for (int n = 0; n < nbNeighbors; n++)
11508       {
11509         int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
11510         const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11511         if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
11512         {
11513           bool ok = false;
11514           for ( size_t idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
11515           {
11516             // MESSAGE("Domain " << idombis);
11517             const TIDSortedElemSet& domainbis = theElems[idombis];
11518             if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11519           }
11520           if ( ok || onAllBoundaries ) // the characteristics of the face is stored
11521           {
11522             DownIdType face(downIds[n], downTypes[n]);
11523             if (!faceDomains[face].count(idom))
11524             {
11525               faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11526               celldom[vtkId] = idom;
11527               //MESSAGE("       cell with a border " << vtkId << " domain " << idom);
11528             }
11529             if ( !ok )
11530             {
11531               theRestDomElems.insert( elem );
11532               faceDomains[face][iRestDom] = neighborsVtkIds[n];
11533               celldom[neighborsVtkIds[n]] = iRestDom;
11534             }
11535           }
11536         }
11537       }
11538     }
11539   }
11540
11541   //MESSAGE("Number of shared faces " << faceDomains.size());
11542   std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11543
11544   // --- explore the shared faces domain by domain,
11545   //     explore the nodes of the face and see if they belong to a cell in the domain,
11546   //     which has only a node or an edge on the border (not a shared face)
11547
11548   for (int idomain = idom0; idomain < nbDomains; idomain++)
11549   {
11550     //MESSAGE("Domain " << idomain);
11551     const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
11552     itface = faceDomains.begin();
11553     for (; itface != faceDomains.end(); ++itface)
11554     {
11555       const std::map<int, int>& domvol = itface->second;
11556       if (!domvol.count(idomain))
11557         continue;
11558       DownIdType face = itface->first;
11559       //MESSAGE(" --- face " << face.cellId);
11560       std::set<int> oldNodes;
11561       oldNodes.clear();
11562       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11563       std::set<int>::iterator itn = oldNodes.begin();
11564       for (; itn != oldNodes.end(); ++itn)
11565       {
11566         int oldId = *itn;
11567         //MESSAGE("     node " << oldId);
11568         vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11569         for (int i=0; i<l.ncells; i++)
11570         {
11571           int vtkId = l.cells[i];
11572           const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
11573           if (!domain.count(anElem))
11574             continue;
11575           int vtkType = grid->GetCellType(vtkId);
11576           int downId = grid->CellIdToDownId(vtkId);
11577           if (downId < 0)
11578           {
11579             MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11580             continue; // not OK at this stage of the algorithm:
11581             //no cells created after BuildDownWardConnectivity
11582           }
11583           DownIdType aCell(downId, vtkType);
11584           cellDomains[aCell][idomain] = vtkId;
11585           celldom[vtkId] = idomain;
11586           //MESSAGE("       cell " << vtkId << " domain " << idomain);
11587         }
11588       }
11589     }
11590   }
11591
11592   // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11593   //     for each shared face, get the nodes
11594   //     for each node, for each domain of the face, create a clone of the node
11595
11596   // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11597   //     junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11598   //     the value is the ordered domain ids. (more than 4 domains not taken into account)
11599
11600   std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11601   std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11602   std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11603
11604   //MESSAGE(".. Duplication of the nodes");
11605   for (int idomain = idom0; idomain < nbDomains; idomain++)
11606   {
11607     itface = faceDomains.begin();
11608     for (; itface != faceDomains.end(); ++itface)
11609     {
11610       const std::map<int, int>& domvol = itface->second;
11611       if (!domvol.count(idomain))
11612         continue;
11613       DownIdType face = itface->first;
11614       //MESSAGE(" --- face " << face.cellId);
11615       std::set<int> oldNodes;
11616       oldNodes.clear();
11617       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11618       std::set<int>::iterator itn = oldNodes.begin();
11619       for (; itn != oldNodes.end(); ++itn)
11620       {
11621         int oldId = *itn;
11622         if (nodeDomains[oldId].empty())
11623         {
11624           nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11625           //MESSAGE("-+-+-b     oldNode " << oldId << " domain " << idomain);
11626         }
11627         std::map<int, int>::const_iterator itdom = domvol.begin();
11628         for (; itdom != domvol.end(); ++itdom)
11629         {
11630           int idom = itdom->first;
11631           //MESSAGE("         domain " << idom);
11632           if (!nodeDomains[oldId].count(idom)) // --- node to clone
11633           {
11634             if (nodeDomains[oldId].size() >= 2) // a multiple node
11635             {
11636               vector<int> orderedDoms;
11637               //MESSAGE("multiple node " << oldId);
11638               if (mutipleNodes.count(oldId))
11639                 orderedDoms = mutipleNodes[oldId];
11640               else
11641               {
11642                 map<int,int>::iterator it = nodeDomains[oldId].begin();
11643                 for (; it != nodeDomains[oldId].end(); ++it)
11644                   orderedDoms.push_back(it->first);
11645               }
11646               orderedDoms.push_back(idom); // TODO order ==> push_front or back
11647               //stringstream txt;
11648               //for (int i=0; i<orderedDoms.size(); i++)
11649               //  txt << orderedDoms[i] << " ";
11650               //MESSAGE("orderedDoms " << txt.str());
11651               mutipleNodes[oldId] = orderedDoms;
11652             }
11653             double *coords = grid->GetPoint(oldId);
11654             SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11655             copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11656             int newId = newNode->getVtkId();
11657             nodeDomains[oldId][idom] = newId; // cloned node for other domains
11658             //MESSAGE("-+-+-c     oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11659           }
11660         }
11661       }
11662     }
11663   }
11664
11665   //MESSAGE(".. Creation of elements");
11666   for (int idomain = idom0; idomain < nbDomains; idomain++)
11667   {
11668     itface = faceDomains.begin();
11669     for (; itface != faceDomains.end(); ++itface)
11670     {
11671       std::map<int, int> domvol = itface->second;
11672       if (!domvol.count(idomain))
11673         continue;
11674       DownIdType face = itface->first;
11675       //MESSAGE(" --- face " << face.cellId);
11676       std::set<int> oldNodes;
11677       oldNodes.clear();
11678       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11679       int nbMultipleNodes = 0;
11680       std::set<int>::iterator itn = oldNodes.begin();
11681       for (; itn != oldNodes.end(); ++itn)
11682       {
11683         int oldId = *itn;
11684         if (mutipleNodes.count(oldId))
11685           nbMultipleNodes++;
11686       }
11687       if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11688       {
11689         //MESSAGE("multiple Nodes detected on a shared face");
11690         int downId = itface->first.cellId;
11691         unsigned char cellType = itface->first.cellType;
11692         // --- shared edge or shared face ?
11693         if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11694         {
11695           int nodes[3];
11696           int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11697           for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11698             if (mutipleNodes.count(nodes[i]))
11699               if (!mutipleNodesToFace.count(nodes[i]))
11700                 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11701         }
11702         else // shared face (between two volumes)
11703         {
11704           int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11705           const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11706           const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11707           for (int ie =0; ie < nbEdges; ie++)
11708           {
11709             int nodes[3];
11710             int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11711             if ( mutipleNodes.count(nodes[0]) && mutipleNodes.count( nodes[ nbNodes-1 ]))
11712             {
11713               vector<int> vn0 = mutipleNodes[nodes[0]];
11714               vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11715               vector<int> doms;
11716               for ( size_t i0 = 0; i0 < vn0.size(); i0++ )
11717                 for ( size_t i1 = 0; i1 < vn1.size(); i1++ )
11718                   if ( vn0[i0] == vn1[i1] )
11719                     doms.push_back( vn0[ i0 ]);
11720               if ( doms.size() > 2 )
11721               {
11722                 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11723                 double *coords = grid->GetPoint(nodes[0]);
11724                 gp_Pnt p0(coords[0], coords[1], coords[2]);
11725                 coords = grid->GetPoint(nodes[nbNodes - 1]);
11726                 gp_Pnt p1(coords[0], coords[1], coords[2]);
11727                 gp_Pnt gref;
11728                 int vtkVolIds[1000];  // an edge can belong to a lot of volumes
11729                 map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
11730                 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11731                 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11732                 for ( size_t id = 0; id < doms.size(); id++ )
11733                 {
11734                   int idom = doms[id];
11735                   const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11736                   for ( int ivol = 0; ivol < nbvol; ivol++ )
11737                   {
11738                     int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
11739                     SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
11740                     if (domain.count(elem))
11741                     {
11742                       SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
11743                       domvol[idom] = svol;
11744                       //MESSAGE("  domain " << idom << " volume " << elem->GetID());
11745                       double values[3];
11746                       vtkIdType npts = 0;
11747                       vtkIdType* pts = 0;
11748                       grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11749                       SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
11750                       if (id ==0)
11751                       {
11752                         gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
11753                         angleDom[idom] = 0;
11754                       }
11755                       else
11756                       {
11757                         gp_Pnt g(values[0], values[1], values[2]);
11758                         angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11759                         //MESSAGE("  angle=" << angleDom[idom]);
11760                       }
11761                       break;
11762                     }
11763                   }
11764                 }
11765                 map<double, int> sortedDom; // sort domains by angle
11766                 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11767                   sortedDom[ia->second] = ia->first;
11768                 vector<int> vnodes;
11769                 vector<int> vdom;
11770                 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11771                 {
11772                   vdom.push_back(ib->second);
11773                   //MESSAGE("  ordered domain " << ib->second << "  angle " << ib->first);
11774                 }
11775                 for (int ino = 0; ino < nbNodes; ino++)
11776                   vnodes.push_back(nodes[ino]);
11777                 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11778               }
11779             }
11780           }
11781         }
11782       }
11783     }
11784   }
11785
11786   // --- iterate on shared faces (volumes to modify, face to extrude)
11787   //     get node id's of the face (id SMDS = id VTK)
11788   //     create flat element with old and new nodes if requested
11789
11790   // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11791   //     (domain1 X domain2) = domain1 + MAXINT*domain2
11792
11793   std::map<int, std::map<long,int> > nodeQuadDomains;
11794   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11795
11796   //MESSAGE(".. Creation of elements: simple junction");
11797   if (createJointElems)
11798   {
11799     int idg;
11800     string joints2DName = "joints2D";
11801     mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
11802     SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11803     string joints3DName = "joints3D";
11804     mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
11805     SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11806
11807     itface = faceDomains.begin();
11808     for (; itface != faceDomains.end(); ++itface)
11809     {
11810       DownIdType face = itface->first;
11811       std::set<int> oldNodes;
11812       std::set<int>::iterator itn;
11813       oldNodes.clear();
11814       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11815
11816       std::map<int, int> domvol = itface->second;
11817       std::map<int, int>::iterator itdom = domvol.begin();
11818       int dom1 = itdom->first;
11819       int vtkVolId = itdom->second;
11820       itdom++;
11821       int dom2 = itdom->first;
11822       SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11823                                                        nodeQuadDomains);
11824       stringstream grpname;
11825       grpname << "j_";
11826       if (dom1 < dom2)
11827         grpname << dom1 << "_" << dom2;
11828       else
11829         grpname << dom2 << "_" << dom1;
11830       string namegrp = grpname.str();
11831       if (!mapOfJunctionGroups.count(namegrp))
11832         mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
11833       SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11834       if (sgrp)
11835         sgrp->Add(vol->GetID());
11836       if (vol->GetType() == SMDSAbs_Volume)
11837         joints3DGrp->Add(vol->GetID());
11838       else if (vol->GetType() == SMDSAbs_Face)
11839         joints2DGrp->Add(vol->GetID());
11840     }
11841   }
11842
11843   // --- create volumes on multiple domain intersection if requested
11844   //     iterate on mutipleNodesToFace
11845   //     iterate on edgesMultiDomains
11846
11847   //MESSAGE(".. Creation of elements: multiple junction");
11848   if (createJointElems)
11849   {
11850     // --- iterate on mutipleNodesToFace
11851
11852     std::map<int, std::vector<int> >::iterator itn =  mutipleNodesToFace.begin();
11853     for (; itn != mutipleNodesToFace.end(); ++itn)
11854     {
11855       int node = itn->first;
11856       vector<int> orderDom = itn->second;
11857       vector<vtkIdType> orderedNodes;
11858       for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11859         orderedNodes.push_back( nodeDomains[ node ][ orderDom[ idom ]]);
11860       SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11861
11862       stringstream grpname;
11863       grpname << "m2j_";
11864       grpname << 0 << "_" << 0;
11865       int idg;
11866       string namegrp = grpname.str();
11867       if (!mapOfJunctionGroups.count(namegrp))
11868         mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
11869       SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11870       if (sgrp)
11871         sgrp->Add(face->GetID());
11872     }
11873
11874     // --- iterate on edgesMultiDomains
11875
11876     std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11877     for (; ite != edgesMultiDomains.end(); ++ite)
11878     {
11879       vector<int> nodes = ite->first;
11880       vector<int> orderDom = ite->second;
11881       vector<vtkIdType> orderedNodes;
11882       if (nodes.size() == 2)
11883       {
11884         //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11885         for ( size_t ino = 0; ino < nodes.size(); ino++ )
11886           if ( orderDom.size() == 3 )
11887             for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11888               orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11889           else
11890             for (int idom = orderDom.size()-1; idom >=0; idom--)
11891               orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11892         SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11893
11894         int idg;
11895         string namegrp = "jointsMultiples";
11896         if (!mapOfJunctionGroups.count(namegrp))
11897           mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11898         SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11899         if (sgrp)
11900           sgrp->Add(vol->GetID());
11901       }
11902       else
11903       {
11904         //INFOS("Quadratic multiple joints not implemented");
11905         // TODO quadratic nodes
11906       }
11907     }
11908   }
11909
11910   // --- list the explicit faces and edges of the mesh that need to be modified,
11911   //     i.e. faces and edges built with one or more duplicated nodes.
11912   //     associate these faces or edges to their corresponding domain.
11913   //     only the first domain found is kept when a face or edge is shared
11914
11915   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11916   std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11917   faceOrEdgeDom.clear();
11918   feDom.clear();
11919
11920   //MESSAGE(".. Modification of elements");
11921   for (int idomain = idom0; idomain < nbDomains; idomain++)
11922   {
11923     std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11924     for (; itnod != nodeDomains.end(); ++itnod)
11925     {
11926       int oldId = itnod->first;
11927       //MESSAGE("     node " << oldId);
11928       vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11929       for (int i = 0; i < l.ncells; i++)
11930       {
11931         int vtkId = l.cells[i];
11932         int vtkType = grid->GetCellType(vtkId);
11933         int downId = grid->CellIdToDownId(vtkId);
11934         if (downId < 0)
11935           continue; // new cells: not to be modified
11936         DownIdType aCell(downId, vtkType);
11937         int volParents[1000];
11938         int nbvol = grid->GetParentVolumes(volParents, vtkId);
11939         for (int j = 0; j < nbvol; j++)
11940           if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11941             if (!feDom.count(vtkId))
11942             {
11943               feDom[vtkId] = idomain;
11944               faceOrEdgeDom[aCell] = emptyMap;
11945               faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11946               //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
11947               //        << " type " << vtkType << " downId " << downId);
11948             }
11949       }
11950     }
11951   }
11952
11953   // --- iterate on shared faces (volumes to modify, face to extrude)
11954   //     get node id's of the face
11955   //     replace old nodes by new nodes in volumes, and update inverse connectivity
11956
11957   std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11958   for (int m=0; m<3; m++)
11959   {
11960     std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11961     itface = (*amap).begin();
11962     for (; itface != (*amap).end(); ++itface)
11963     {
11964       DownIdType face = itface->first;
11965       std::set<int> oldNodes;
11966       std::set<int>::iterator itn;
11967       oldNodes.clear();
11968       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11969       //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11970       std::map<int, int> localClonedNodeIds;
11971
11972       std::map<int, int> domvol = itface->second;
11973       std::map<int, int>::iterator itdom = domvol.begin();
11974       for (; itdom != domvol.end(); ++itdom)
11975       {
11976         int idom = itdom->first;
11977         int vtkVolId = itdom->second;
11978         //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
11979         localClonedNodeIds.clear();
11980         for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11981         {
11982           int oldId = *itn;
11983           if (nodeDomains[oldId].count(idom))
11984           {
11985             localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11986             //MESSAGE("     node " << oldId << " --> " << localClonedNodeIds[oldId]);
11987           }
11988         }
11989         meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11990       }
11991     }
11992   }
11993
11994   // Remove empty groups (issue 0022812)
11995   std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
11996   for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
11997   {
11998     if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
11999       myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
12000   }
12001
12002   meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
12003   grid->DeleteLinks();
12004
12005   CHRONOSTOP(50);
12006   counters::stats();
12007   return true;
12008 }
12009
12010 /*!
12011  * \brief Double nodes on some external faces and create flat elements.
12012  * Flat elements are mainly used by some types of mechanic calculations.
12013  *
12014  * Each group of the list must be constituted of faces.
12015  * Triangles are transformed in prisms, and quadrangles in hexahedrons.
12016  * @param theElems - list of groups of faces, where a group of faces is a set of
12017  * SMDS_MeshElements sorted by Id.
12018  * @return TRUE if operation has been completed successfully, FALSE otherwise
12019  */
12020 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
12021 {
12022   // MESSAGE("-------------------------------------------------");
12023   // MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
12024   // MESSAGE("-------------------------------------------------");
12025
12026   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
12027
12028   // --- For each group of faces
12029   //     duplicate the nodes, create a flat element based on the face
12030   //     replace the nodes of the faces by their clones
12031
12032   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
12033   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
12034   clonedNodes.clear();
12035   intermediateNodes.clear();
12036   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
12037   mapOfJunctionGroups.clear();
12038
12039   for ( size_t idom = 0; idom < theElems.size(); idom++ )
12040   {
12041     const TIDSortedElemSet&           domain = theElems[idom];
12042     TIDSortedElemSet::const_iterator elemItr = domain.begin();
12043     for ( ; elemItr != domain.end(); ++elemItr )
12044     {
12045       SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
12046       SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
12047       if (!aFace)
12048         continue;
12049       // MESSAGE("aFace=" << aFace->GetID());
12050       bool isQuad = aFace->IsQuadratic();
12051       vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
12052
12053       // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
12054
12055       SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
12056       while (nodeIt->more())
12057       {
12058         const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
12059         bool isMedium = isQuad && (aFace->IsMediumNode(node));
12060         if (isMedium)
12061           ln2.push_back(node);
12062         else
12063           ln0.push_back(node);
12064
12065         const SMDS_MeshNode* clone = 0;
12066         if (!clonedNodes.count(node))
12067         {
12068           clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
12069           copyPosition( node, clone );
12070           clonedNodes[node] = clone;
12071         }
12072         else
12073           clone = clonedNodes[node];
12074
12075         if (isMedium)
12076           ln3.push_back(clone);
12077         else
12078           ln1.push_back(clone);
12079
12080         const SMDS_MeshNode* inter = 0;
12081         if (isQuad && (!isMedium))
12082         {
12083           if (!intermediateNodes.count(node))
12084           {
12085             inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
12086             copyPosition( node, inter );
12087             intermediateNodes[node] = inter;
12088           }
12089           else
12090             inter = intermediateNodes[node];
12091           ln4.push_back(inter);
12092         }
12093       }
12094
12095       // --- extrude the face
12096
12097       vector<const SMDS_MeshNode*> ln;
12098       SMDS_MeshVolume* vol = 0;
12099       vtkIdType aType = aFace->GetVtkType();
12100       switch (aType)
12101       {
12102       case VTK_TRIANGLE:
12103         vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
12104         // MESSAGE("vol prism " << vol->GetID());
12105         ln.push_back(ln1[0]);
12106         ln.push_back(ln1[1]);
12107         ln.push_back(ln1[2]);
12108         break;
12109       case VTK_QUAD:
12110         vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
12111         // MESSAGE("vol hexa " << vol->GetID());
12112         ln.push_back(ln1[0]);
12113         ln.push_back(ln1[1]);
12114         ln.push_back(ln1[2]);
12115         ln.push_back(ln1[3]);
12116         break;
12117       case VTK_QUADRATIC_TRIANGLE:
12118         vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
12119                                 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
12120         // MESSAGE("vol quad prism " << vol->GetID());
12121         ln.push_back(ln1[0]);
12122         ln.push_back(ln1[1]);
12123         ln.push_back(ln1[2]);
12124         ln.push_back(ln3[0]);
12125         ln.push_back(ln3[1]);
12126         ln.push_back(ln3[2]);
12127         break;
12128       case VTK_QUADRATIC_QUAD:
12129         //              vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
12130         //                                      ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
12131         //                                      ln4[0], ln4[1], ln4[2], ln4[3]);
12132         vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
12133                                 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
12134                                 ln4[0], ln4[1], ln4[2], ln4[3]);
12135         // MESSAGE("vol quad hexa " << vol->GetID());
12136         ln.push_back(ln1[0]);
12137         ln.push_back(ln1[1]);
12138         ln.push_back(ln1[2]);
12139         ln.push_back(ln1[3]);
12140         ln.push_back(ln3[0]);
12141         ln.push_back(ln3[1]);
12142         ln.push_back(ln3[2]);
12143         ln.push_back(ln3[3]);
12144         break;
12145       case VTK_POLYGON:
12146         break;
12147       default:
12148         break;
12149       }
12150
12151       if (vol)
12152       {
12153         stringstream grpname;
12154         grpname << "jf_";
12155         grpname << idom;
12156         int idg;
12157         string namegrp = grpname.str();
12158         if (!mapOfJunctionGroups.count(namegrp))
12159           mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
12160         SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
12161         if (sgrp)
12162           sgrp->Add(vol->GetID());
12163       }
12164
12165       // --- modify the face
12166
12167       aFace->ChangeNodes(&ln[0], ln.size());
12168     }
12169   }
12170   return true;
12171 }
12172
12173 /*!
12174  *  \brief identify all the elements around a geom shape, get the faces delimiting the hole
12175  *  Build groups of volume to remove, groups of faces to replace on the skin of the object,
12176  *  groups of faces to remove inside the object, (idem edges).
12177  *  Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
12178  */
12179 void SMESH_MeshEditor::CreateHoleSkin(double                          radius,
12180                                       const TopoDS_Shape&             theShape,
12181                                       SMESH_NodeSearcher*             theNodeSearcher,
12182                                       const char*                     groupName,
12183                                       std::vector<double>&            nodesCoords,
12184                                       std::vector<std::vector<int> >& listOfListOfNodes)
12185 {
12186   // MESSAGE("--------------------------------");
12187   // MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
12188   // MESSAGE("--------------------------------");
12189
12190   // --- zone of volumes to remove is given :
12191   //     1 either by a geom shape (one or more vertices) and a radius,
12192   //     2 either by a group of nodes (representative of the shape)to use with the radius,
12193   //     3 either by a group of nodes where all the elements build on one of this nodes are to remove,
12194   //     In the case 2, the group of nodes is an external group of nodes from another mesh,
12195   //     In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
12196   //     defined by it's name.
12197
12198   SMESHDS_GroupBase* groupDS = 0;
12199   SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
12200   while ( groupIt->more() )
12201   {
12202     groupDS = 0;
12203     SMESH_Group * group = groupIt->next();
12204     if ( !group ) continue;
12205     groupDS = group->GetGroupDS();
12206     if ( !groupDS || groupDS->IsEmpty() ) continue;
12207     std::string grpName = group->GetName();
12208     //MESSAGE("grpName=" << grpName);
12209     if (grpName == groupName)
12210       break;
12211     else
12212       groupDS = 0;
12213   }
12214
12215   bool isNodeGroup = false;
12216   bool isNodeCoords = false;
12217   if (groupDS)
12218   {
12219     if (groupDS->GetType() != SMDSAbs_Node)
12220       return;
12221     isNodeGroup = true;     // a group of nodes exists and it is in this mesh
12222   }
12223
12224   if (nodesCoords.size() > 0)
12225     isNodeCoords = true; // a list o nodes given by their coordinates
12226   //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
12227
12228   // --- define groups to build
12229
12230   int idg; // --- group of SMDS volumes
12231   string grpvName = groupName;
12232   grpvName += "_vol";
12233   SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
12234   if (!grp)
12235   {
12236     MESSAGE("group not created " << grpvName);
12237     return;
12238   }
12239   SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
12240
12241   int idgs; // --- group of SMDS faces on the skin
12242   string grpsName = groupName;
12243   grpsName += "_skin";
12244   SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
12245   if (!grps)
12246   {
12247     MESSAGE("group not created " << grpsName);
12248     return;
12249   }
12250   SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
12251
12252   int idgi; // --- group of SMDS faces internal (several shapes)
12253   string grpiName = groupName;
12254   grpiName += "_internalFaces";
12255   SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
12256   if (!grpi)
12257   {
12258     MESSAGE("group not created " << grpiName);
12259     return;
12260   }
12261   SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
12262
12263   int idgei; // --- group of SMDS faces internal (several shapes)
12264   string grpeiName = groupName;
12265   grpeiName += "_internalEdges";
12266   SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
12267   if (!grpei)
12268   {
12269     MESSAGE("group not created " << grpeiName);
12270     return;
12271   }
12272   SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
12273
12274   // --- build downward connectivity
12275
12276   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
12277   meshDS->BuildDownWardConnectivity(true);
12278   SMDS_UnstructuredGrid* grid = meshDS->getGrid();
12279
12280   // --- set of volumes detected inside
12281
12282   std::set<int> setOfInsideVol;
12283   std::set<int> setOfVolToCheck;
12284
12285   std::vector<gp_Pnt> gpnts;
12286   gpnts.clear();
12287
12288   if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
12289   {
12290     //MESSAGE("group of nodes provided");
12291     SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
12292     while ( elemIt->more() )
12293     {
12294       const SMDS_MeshElement* elem = elemIt->next();
12295       if (!elem)
12296         continue;
12297       const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
12298       if (!node)
12299         continue;
12300       SMDS_MeshElement* vol = 0;
12301       SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
12302       while (volItr->more())
12303       {
12304         vol = (SMDS_MeshElement*)volItr->next();
12305         setOfInsideVol.insert(vol->getVtkId());
12306         sgrp->Add(vol->GetID());
12307       }
12308     }
12309   }
12310   else if (isNodeCoords)
12311   {
12312     //MESSAGE("list of nodes coordinates provided");
12313     size_t i = 0;
12314     int k = 0;
12315     while ( i < nodesCoords.size()-2 )
12316     {
12317       double x = nodesCoords[i++];
12318       double y = nodesCoords[i++];
12319       double z = nodesCoords[i++];
12320       gp_Pnt p = gp_Pnt(x, y ,z);
12321       gpnts.push_back(p);
12322       //MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
12323       k++;
12324     }
12325   }
12326   else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
12327   {
12328     //MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
12329     TopTools_IndexedMapOfShape vertexMap;
12330     TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
12331     gp_Pnt p = gp_Pnt(0,0,0);
12332     if (vertexMap.Extent() < 1)
12333       return;
12334
12335     for ( int i = 1; i <= vertexMap.Extent(); ++i )
12336     {
12337       const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
12338       p = BRep_Tool::Pnt(vertex);
12339       gpnts.push_back(p);
12340       //MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
12341     }
12342   }
12343
12344   if (gpnts.size() > 0)
12345   {
12346     const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
12347     //MESSAGE("startNode->nodeId " << nodeId);
12348
12349     double radius2 = radius*radius;
12350     //MESSAGE("radius2 " << radius2);
12351
12352     // --- volumes on start node
12353
12354     setOfVolToCheck.clear();
12355     SMDS_MeshElement* startVol = 0;
12356     SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
12357     while (volItr->more())
12358     {
12359       startVol = (SMDS_MeshElement*)volItr->next();
12360       setOfVolToCheck.insert(startVol->getVtkId());
12361     }
12362     if (setOfVolToCheck.empty())
12363     {
12364       MESSAGE("No volumes found");
12365       return;
12366     }
12367
12368     // --- starting with central volumes then their neighbors, check if they are inside
12369     //     or outside the domain, until no more new neighbor volume is inside.
12370     //     Fill the group of inside volumes
12371
12372     std::map<int, double> mapOfNodeDistance2;
12373     mapOfNodeDistance2.clear();
12374     std::set<int> setOfOutsideVol;
12375     while (!setOfVolToCheck.empty())
12376     {
12377       std::set<int>::iterator it = setOfVolToCheck.begin();
12378       int vtkId = *it;
12379       //MESSAGE("volume to check,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12380       bool volInside = false;
12381       vtkIdType npts = 0;
12382       vtkIdType* pts = 0;
12383       grid->GetCellPoints(vtkId, npts, pts);
12384       for (int i=0; i<npts; i++)
12385       {
12386         double distance2 = 0;
12387         if (mapOfNodeDistance2.count(pts[i]))
12388         {
12389           distance2 = mapOfNodeDistance2[pts[i]];
12390           //MESSAGE("point " << pts[i] << " distance2 " << distance2);
12391         }
12392         else
12393         {
12394           double *coords = grid->GetPoint(pts[i]);
12395           gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
12396           distance2 = 1.E40;
12397           for ( size_t j = 0; j < gpnts.size(); j++ )
12398           {
12399             double d2 = aPoint.SquareDistance( gpnts[ j ]);
12400             if (d2 < distance2)
12401             {
12402               distance2 = d2;
12403               if (distance2 < radius2)
12404                 break;
12405             }
12406           }
12407           mapOfNodeDistance2[pts[i]] = distance2;
12408           //MESSAGE("  point "  << pts[i]  << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " <<  coords[2]);
12409         }
12410         if (distance2 < radius2)
12411         {
12412           volInside = true; // one or more nodes inside the domain
12413           sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12414           break;
12415         }
12416       }
12417       if (volInside)
12418       {
12419         setOfInsideVol.insert(vtkId);
12420         //MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12421         int neighborsVtkIds[NBMAXNEIGHBORS];
12422         int downIds[NBMAXNEIGHBORS];
12423         unsigned char downTypes[NBMAXNEIGHBORS];
12424         int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12425         for (int n = 0; n < nbNeighbors; n++)
12426           if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12427             setOfVolToCheck.insert(neighborsVtkIds[n]);
12428       }
12429       else
12430       {
12431         setOfOutsideVol.insert(vtkId);
12432         //MESSAGE("  volume outside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12433       }
12434       setOfVolToCheck.erase(vtkId);
12435     }
12436   }
12437
12438   // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12439   //     If yes, add the volume to the inside set
12440
12441   bool addedInside = true;
12442   std::set<int> setOfVolToReCheck;
12443   while (addedInside)
12444   {
12445     //MESSAGE(" --------------------------- re check");
12446     addedInside = false;
12447     std::set<int>::iterator itv = setOfInsideVol.begin();
12448     for (; itv != setOfInsideVol.end(); ++itv)
12449     {
12450       int vtkId = *itv;
12451       int neighborsVtkIds[NBMAXNEIGHBORS];
12452       int downIds[NBMAXNEIGHBORS];
12453       unsigned char downTypes[NBMAXNEIGHBORS];
12454       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12455       for (int n = 0; n < nbNeighbors; n++)
12456         if (!setOfInsideVol.count(neighborsVtkIds[n]))
12457           setOfVolToReCheck.insert(neighborsVtkIds[n]);
12458     }
12459     setOfVolToCheck = setOfVolToReCheck;
12460     setOfVolToReCheck.clear();
12461     while  (!setOfVolToCheck.empty())
12462     {
12463       std::set<int>::iterator it = setOfVolToCheck.begin();
12464       int vtkId = *it;
12465       if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12466       {
12467         //MESSAGE("volume to recheck,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12468         int countInside = 0;
12469         int neighborsVtkIds[NBMAXNEIGHBORS];
12470         int downIds[NBMAXNEIGHBORS];
12471         unsigned char downTypes[NBMAXNEIGHBORS];
12472         int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12473         for (int n = 0; n < nbNeighbors; n++)
12474           if (setOfInsideVol.count(neighborsVtkIds[n]))
12475             countInside++;
12476         //MESSAGE("countInside " << countInside);
12477         if (countInside > 1)
12478         {
12479           //MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12480           setOfInsideVol.insert(vtkId);
12481           sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12482           addedInside = true;
12483         }
12484         else
12485           setOfVolToReCheck.insert(vtkId);
12486       }
12487       setOfVolToCheck.erase(vtkId);
12488     }
12489   }
12490
12491   // --- map of Downward faces at the boundary, inside the global volume
12492   //     map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12493   //     fill group of SMDS faces inside the volume (when several volume shapes)
12494   //     fill group of SMDS faces on the skin of the global volume (if skin)
12495
12496   std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12497   std::map<DownIdType, int, DownIdCompare> skinFaces;     // faces on the skin of the global volume --> corresponding cell
12498   std::set<int>::iterator it = setOfInsideVol.begin();
12499   for (; it != setOfInsideVol.end(); ++it)
12500   {
12501     int vtkId = *it;
12502     //MESSAGE("  vtkId " << vtkId  << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12503     int neighborsVtkIds[NBMAXNEIGHBORS];
12504     int downIds[NBMAXNEIGHBORS];
12505     unsigned char downTypes[NBMAXNEIGHBORS];
12506     int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12507     for (int n = 0; n < nbNeighbors; n++)
12508     {
12509       int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12510       if (neighborDim == 3)
12511       {
12512         if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12513         {
12514           DownIdType face(downIds[n], downTypes[n]);
12515           boundaryFaces[face] = vtkId;
12516         }
12517         // if the face between to volumes is in the mesh, get it (internal face between shapes)
12518         int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12519         if (vtkFaceId >= 0)
12520         {
12521           sgrpi->Add(meshDS->fromVtkToSmds(vtkFaceId));
12522           // find also the smds edges on this face
12523           int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12524           const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12525           const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12526           for (int i = 0; i < nbEdges; i++)
12527           {
12528             int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12529             if (vtkEdgeId >= 0)
12530               sgrpei->Add(meshDS->fromVtkToSmds(vtkEdgeId));
12531           }
12532         }
12533       }
12534       else if (neighborDim == 2) // skin of the volume
12535       {
12536         DownIdType face(downIds[n], downTypes[n]);
12537         skinFaces[face] = vtkId;
12538         int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12539         if (vtkFaceId >= 0)
12540           sgrps->Add(meshDS->fromVtkToSmds(vtkFaceId));
12541       }
12542     }
12543   }
12544
12545   // --- identify the edges constituting the wire of each subshape on the skin
12546   //     define polylines with the nodes of edges, equivalent to wires
12547   //     project polylines on subshapes, and partition, to get geom faces
12548
12549   std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12550   std::set<int> emptySet;
12551   emptySet.clear();
12552   std::set<int> shapeIds;
12553
12554   SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12555   while (itelem->more())
12556   {
12557     const SMDS_MeshElement *elem = itelem->next();
12558     int shapeId = elem->getshapeId();
12559     int vtkId = elem->getVtkId();
12560     if (!shapeIdToVtkIdSet.count(shapeId))
12561     {
12562       shapeIdToVtkIdSet[shapeId] = emptySet;
12563       shapeIds.insert(shapeId);
12564     }
12565     shapeIdToVtkIdSet[shapeId].insert(vtkId);
12566   }
12567
12568   std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12569   std::set<DownIdType, DownIdCompare> emptyEdges;
12570   emptyEdges.clear();
12571
12572   std::map<int, std::set<int> >::iterator itShape =  shapeIdToVtkIdSet.begin();
12573   for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12574   {
12575     int shapeId = itShape->first;
12576     //MESSAGE(" --- Shape ID --- "<< shapeId);
12577     shapeIdToEdges[shapeId] = emptyEdges;
12578
12579     std::vector<int> nodesEdges;
12580
12581     std::set<int>::iterator its = itShape->second.begin();
12582     for (; its != itShape->second.end(); ++its)
12583     {
12584       int vtkId = *its;
12585       //MESSAGE("     " << vtkId);
12586       int neighborsVtkIds[NBMAXNEIGHBORS];
12587       int downIds[NBMAXNEIGHBORS];
12588       unsigned char downTypes[NBMAXNEIGHBORS];
12589       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12590       for (int n = 0; n < nbNeighbors; n++)
12591       {
12592         if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12593           continue;
12594         int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
12595         const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12596         if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12597         {
12598           DownIdType edge(downIds[n], downTypes[n]);
12599           if (!shapeIdToEdges[shapeId].count(edge))
12600           {
12601             shapeIdToEdges[shapeId].insert(edge);
12602             int vtkNodeId[3];
12603             int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12604             nodesEdges.push_back(vtkNodeId[0]);
12605             nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12606             //MESSAGE("       --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12607           }
12608         }
12609       }
12610     }
12611
12612     std::list<int> order;
12613     order.clear();
12614     if (nodesEdges.size() > 0)
12615     {
12616       order.push_back(nodesEdges[0]); //MESSAGE("       --- back " << order.back()+1); // SMDS id = VTK id + 1;
12617       nodesEdges[0] = -1;
12618       order.push_back(nodesEdges[1]); //MESSAGE("       --- back " << order.back()+1);
12619       nodesEdges[1] = -1; // do not reuse this edge
12620       bool found = true;
12621       while (found)
12622       {
12623         int nodeTofind = order.back(); // try first to push back
12624         int i = 0;
12625         for ( i = 0; i < (int)nodesEdges.size(); i++ )
12626           if (nodesEdges[i] == nodeTofind)
12627             break;
12628         if ( i == (int) nodesEdges.size() )
12629           found = false; // no follower found on back
12630         else
12631         {
12632           if (i%2) // odd ==> use the previous one
12633             if (nodesEdges[i-1] < 0)
12634               found = false;
12635             else
12636             {
12637               order.push_back(nodesEdges[i-1]); //MESSAGE("       --- back " << order.back()+1);
12638               nodesEdges[i-1] = -1;
12639             }
12640           else // even ==> use the next one
12641             if (nodesEdges[i+1] < 0)
12642               found = false;
12643             else
12644             {
12645               order.push_back(nodesEdges[i+1]); //MESSAGE("       --- back " << order.back()+1);
12646               nodesEdges[i+1] = -1;
12647             }
12648         }
12649         if (found)
12650           continue;
12651         // try to push front
12652         found = true;
12653         nodeTofind = order.front(); // try to push front
12654         for ( i = 0;  i < (int)nodesEdges.size(); i++ )
12655           if ( nodesEdges[i] == nodeTofind )
12656             break;
12657         if ( i == (int)nodesEdges.size() )
12658         {
12659           found = false; // no predecessor found on front
12660           continue;
12661         }
12662         if (i%2) // odd ==> use the previous one
12663           if (nodesEdges[i-1] < 0)
12664             found = false;
12665           else
12666           {
12667             order.push_front(nodesEdges[i-1]); //MESSAGE("       --- front " << order.front()+1);
12668             nodesEdges[i-1] = -1;
12669           }
12670         else // even ==> use the next one
12671           if (nodesEdges[i+1] < 0)
12672             found = false;
12673           else
12674           {
12675             order.push_front(nodesEdges[i+1]); //MESSAGE("       --- front " << order.front()+1);
12676             nodesEdges[i+1] = -1;
12677           }
12678       }
12679     }
12680
12681
12682     std::vector<int> nodes;
12683     nodes.push_back(shapeId);
12684     std::list<int>::iterator itl = order.begin();
12685     for (; itl != order.end(); itl++)
12686     {
12687       nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12688       //MESSAGE("              ordered node " << nodes[nodes.size()-1]);
12689     }
12690     listOfListOfNodes.push_back(nodes);
12691   }
12692
12693   //     partition geom faces with blocFissure
12694   //     mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12695   //     mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12696
12697   return;
12698 }
12699
12700
12701 //================================================================================
12702 /*!
12703  * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12704  * The created 2D mesh elements based on nodes of free faces of boundary volumes
12705  * \return TRUE if operation has been completed successfully, FALSE otherwise
12706  */
12707 //================================================================================
12708
12709 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12710 {
12711   // iterates on volume elements and detect all free faces on them
12712   SMESHDS_Mesh* aMesh = GetMeshDS();
12713   if (!aMesh)
12714     return false;
12715
12716   ElemFeatures faceType( SMDSAbs_Face );
12717   int nbFree = 0, nbExisted = 0, nbCreated = 0;
12718   SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12719   while(vIt->more())
12720   {
12721     const SMDS_MeshVolume* volume = vIt->next();
12722     SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12723     vTool.SetExternalNormal();
12724     const int iQuad = volume->IsQuadratic();
12725     faceType.SetQuad( iQuad );
12726     for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12727     {
12728       if (!vTool.IsFreeFace(iface))
12729         continue;
12730       nbFree++;
12731       vector<const SMDS_MeshNode *> nodes;
12732       int nbFaceNodes = vTool.NbFaceNodes(iface);
12733       const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12734       int inode = 0;
12735       for ( ; inode < nbFaceNodes; inode += iQuad+1)
12736         nodes.push_back(faceNodes[inode]);
12737
12738       if (iQuad) // add medium nodes
12739       {
12740         for ( inode = 1; inode < nbFaceNodes; inode += 2)
12741           nodes.push_back(faceNodes[inode]);
12742         if ( nbFaceNodes == 9 ) // bi-quadratic quad
12743           nodes.push_back(faceNodes[8]);
12744       }
12745       // add new face based on volume nodes
12746       if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) )
12747       {
12748         nbExisted++; // face already exsist
12749       }
12750       else
12751       {
12752         AddElement( nodes, faceType.SetPoly( nbFaceNodes/(iQuad+1) > 4 ));
12753         nbCreated++;
12754       }
12755     }
12756   }
12757   return ( nbFree == ( nbExisted + nbCreated ));
12758 }
12759
12760 namespace
12761 {
12762   inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12763   {
12764     if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12765       return n;
12766     return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12767   }
12768 }
12769 //================================================================================
12770 /*!
12771  * \brief Creates missing boundary elements
12772  *  \param elements - elements whose boundary is to be checked
12773  *  \param dimension - defines type of boundary elements to create
12774  *  \param group - a group to store created boundary elements in
12775  *  \param targetMesh - a mesh to store created boundary elements in
12776  *  \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12777  *  \param toCopyExistingBoundary - if true, not only new but also pre-existing
12778  *                                boundary elements will be copied into the targetMesh
12779  *  \param toAddExistingBondary - if true, not only new but also pre-existing
12780  *                                boundary elements will be added into the new group
12781  *  \param aroundElements - if true, elements will be created on boundary of given
12782  *                          elements else, on boundary of the whole mesh.
12783  * \return nb of added boundary elements
12784  */
12785 //================================================================================
12786
12787 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12788                                        Bnd_Dimension           dimension,
12789                                        SMESH_Group*            group/*=0*/,
12790                                        SMESH_Mesh*             targetMesh/*=0*/,
12791                                        bool                    toCopyElements/*=false*/,
12792                                        bool                    toCopyExistingBoundary/*=false*/,
12793                                        bool                    toAddExistingBondary/*= false*/,
12794                                        bool                    aroundElements/*= false*/)
12795 {
12796   SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12797   SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12798   // hope that all elements are of the same type, do not check them all
12799   if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12800     throw SALOME_Exception(LOCALIZED("wrong element type"));
12801
12802   if ( !targetMesh )
12803     toCopyElements = toCopyExistingBoundary = false;
12804
12805   SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12806   SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12807   int nbAddedBnd = 0;
12808
12809   // editor adding present bnd elements and optionally holding elements to add to the group
12810   SMESH_MeshEditor* presentEditor;
12811   SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12812   presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12813
12814   SMESH_MesherHelper helper( *myMesh );
12815   const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12816   SMDS_VolumeTool vTool;
12817   TIDSortedElemSet avoidSet;
12818   const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12819   size_t inode;
12820
12821   typedef vector<const SMDS_MeshNode*> TConnectivity;
12822   TConnectivity tgtNodes;
12823   ElemFeatures elemKind( missType ), elemToCopy;
12824
12825   vector<const SMDS_MeshElement*> presentBndElems;
12826   vector<TConnectivity>           missingBndElems;
12827   vector<int>                     freeFacets;
12828   TConnectivity nodes, elemNodes;
12829
12830   SMDS_ElemIteratorPtr eIt;
12831   if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12832   else                  eIt = SMESHUtils::elemSetIterator( elements );
12833
12834   while (eIt->more())
12835   {
12836     const SMDS_MeshElement* elem = eIt->next();
12837     const int              iQuad = elem->IsQuadratic();
12838     elemKind.SetQuad( iQuad );
12839
12840     // ------------------------------------------------------------------------------------
12841     // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12842     // ------------------------------------------------------------------------------------
12843     presentBndElems.clear();
12844     missingBndElems.clear();
12845     freeFacets.clear(); nodes.clear(); elemNodes.clear();
12846     if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12847     {
12848       const SMDS_MeshElement* otherVol = 0;
12849       for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12850       {
12851         if ( !vTool.IsFreeFace(iface, &otherVol) &&
12852              ( !aroundElements || elements.count( otherVol )))
12853           continue;
12854         freeFacets.push_back( iface );
12855       }
12856       if ( missType == SMDSAbs_Face )
12857         vTool.SetExternalNormal();
12858       for ( size_t i = 0; i < freeFacets.size(); ++i )
12859       {
12860         int                iface = freeFacets[i];
12861         const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12862         const size_t nbFaceNodes = vTool.NbFaceNodes (iface);
12863         if ( missType == SMDSAbs_Edge ) // boundary edges
12864         {
12865           nodes.resize( 2+iQuad );
12866           for ( size_t i = 0; i < nbFaceNodes; i += 1+iQuad )
12867           {
12868             for ( size_t j = 0; j < nodes.size(); ++j )
12869               nodes[ j ] = nn[ i+j ];
12870             if ( const SMDS_MeshElement* edge =
12871                  aMesh->FindElement( nodes, SMDSAbs_Edge, /*noMedium=*/false ))
12872               presentBndElems.push_back( edge );
12873             else
12874               missingBndElems.push_back( nodes );
12875           }
12876         }
12877         else // boundary face
12878         {
12879           nodes.clear();
12880           for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12881             nodes.push_back( nn[inode] ); // add corner nodes
12882           if (iQuad)
12883             for ( inode = 1; inode < nbFaceNodes; inode += 2)
12884               nodes.push_back( nn[inode] ); // add medium nodes
12885           int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12886           if ( iCenter > 0 )
12887             nodes.push_back( vTool.GetNodes()[ iCenter ] );
12888
12889           if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12890                                                                SMDSAbs_Face, /*noMedium=*/false ))
12891             presentBndElems.push_back( f );
12892           else
12893             missingBndElems.push_back( nodes );
12894
12895           if ( targetMesh != myMesh )
12896           {
12897             // add 1D elements on face boundary to be added to a new mesh
12898             const SMDS_MeshElement* edge;
12899             for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12900             {
12901               if ( iQuad )
12902                 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12903               else
12904                 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12905               if ( edge && avoidSet.insert( edge ).second )
12906                 presentBndElems.push_back( edge );
12907             }
12908           }
12909         }
12910       }
12911     }
12912     else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12913     {
12914       avoidSet.clear(), avoidSet.insert( elem );
12915       elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesElemIterator() ),
12916                         SMDS_MeshElement::iterator() );
12917       elemNodes.push_back( elemNodes[0] );
12918       nodes.resize( 2 + iQuad );
12919       const int nbLinks = elem->NbCornerNodes();
12920       for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12921       {
12922         nodes[0] = elemNodes[iN];
12923         nodes[1] = elemNodes[iN+1+iQuad];
12924         if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12925           continue; // not free link
12926
12927         if ( iQuad ) nodes[2] = elemNodes[iN+1];
12928         if ( const SMDS_MeshElement* edge =
12929              aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12930           presentBndElems.push_back( edge );
12931         else
12932           missingBndElems.push_back( nodes );
12933       }
12934     }
12935
12936     // ---------------------------------
12937     // 2. Add missing boundary elements
12938     // ---------------------------------
12939     if ( targetMesh != myMesh )
12940       // instead of making a map of nodes in this mesh and targetMesh,
12941       // we create nodes with same IDs.
12942       for ( size_t i = 0; i < missingBndElems.size(); ++i )
12943       {
12944         TConnectivity& srcNodes = missingBndElems[i];
12945         tgtNodes.resize( srcNodes.size() );
12946         for ( inode = 0; inode < srcNodes.size(); ++inode )
12947           tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12948         if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( tgtNodes,
12949                                                                    missType,
12950                                                                    /*noMedium=*/false))
12951           continue;
12952         tgtEditor.AddElement( tgtNodes, elemKind.SetPoly( tgtNodes.size()/(iQuad+1) > 4 ));
12953         ++nbAddedBnd;
12954       }
12955     else
12956       for ( size_t i = 0; i < missingBndElems.size(); ++i )
12957       {
12958         TConnectivity& nodes = missingBndElems[ i ];
12959         if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12960                                                                    missType,
12961                                                                    /*noMedium=*/false))
12962           continue;
12963         SMDS_MeshElement* newElem =
12964           tgtEditor.AddElement( nodes, elemKind.SetPoly( nodes.size()/(iQuad+1) > 4 ));
12965         nbAddedBnd += bool( newElem );
12966
12967         // try to set a new element to a shape
12968         if ( myMesh->HasShapeToMesh() )
12969         {
12970           bool ok = true;
12971           set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12972           const size_t nbN = nodes.size() / (iQuad+1 );
12973           for ( inode = 0; inode < nbN && ok; ++inode )
12974           {
12975             pair<int, TopAbs_ShapeEnum> i_stype =
12976               helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12977             if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12978               mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12979           }
12980           if ( ok && mediumShapes.size() > 1 )
12981           {
12982             set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12983             pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12984             for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12985             {
12986               if (( ok = ( stype_i->first != stype_i_0.first )))
12987                 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12988                                         aMesh->IndexToShape( stype_i_0.second ));
12989             }
12990           }
12991           if ( ok && mediumShapes.begin()->first == missShapeType )
12992             aMesh->SetMeshElementOnShape( newElem, mediumShapes.begin()->second );
12993         }
12994       }
12995
12996     // ----------------------------------
12997     // 3. Copy present boundary elements
12998     // ----------------------------------
12999     if ( toCopyExistingBoundary )
13000       for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
13001       {
13002         const SMDS_MeshElement* e = presentBndElems[i];
13003         tgtNodes.resize( e->NbNodes() );
13004         for ( inode = 0; inode < tgtNodes.size(); ++inode )
13005           tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
13006         presentEditor->AddElement( tgtNodes, elemToCopy.Init( e ));
13007       }
13008     else // store present elements to add them to a group
13009       for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
13010       {
13011         presentEditor->myLastCreatedElems.push_back( presentBndElems[ i ]);
13012       }
13013
13014   } // loop on given elements
13015
13016   // ---------------------------------------------
13017   // 4. Fill group with boundary elements
13018   // ---------------------------------------------
13019   if ( group )
13020   {
13021     if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
13022       for ( size_t i = 0; i < tgtEditor.myLastCreatedElems.size(); ++i )
13023         g->SMDSGroup().Add( tgtEditor.myLastCreatedElems[ i ]);
13024   }
13025   tgtEditor.myLastCreatedElems.clear();
13026   tgtEditor2.myLastCreatedElems.clear();
13027
13028   // -----------------------
13029   // 5. Copy given elements
13030   // -----------------------
13031   if ( toCopyElements && targetMesh != myMesh )
13032   {
13033     if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
13034     else                  eIt = SMESHUtils::elemSetIterator( elements );
13035     while (eIt->more())
13036     {
13037       const SMDS_MeshElement* elem = eIt->next();
13038       tgtNodes.resize( elem->NbNodes() );
13039       for ( inode = 0; inode < tgtNodes.size(); ++inode )
13040         tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
13041       tgtEditor.AddElement( tgtNodes, elemToCopy.Init( elem ));
13042
13043       tgtEditor.myLastCreatedElems.clear();
13044     }
13045   }
13046   return nbAddedBnd;
13047 }
13048
13049 //================================================================================
13050 /*!
13051  * \brief Copy node position and set \a to node on the same geometry
13052  */
13053 //================================================================================
13054
13055 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
13056                                      const SMDS_MeshNode* to )
13057 {
13058   if ( !from || !to ) return;
13059
13060   SMDS_PositionPtr pos = from->GetPosition();
13061   if ( !pos || from->getshapeId() < 1 ) return;
13062
13063   switch ( pos->GetTypeOfPosition() )
13064   {
13065   case SMDS_TOP_3DSPACE: break;
13066
13067   case SMDS_TOP_FACE:
13068   {
13069     const SMDS_FacePosition* fPos = static_cast< const SMDS_FacePosition* >( pos );
13070     GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
13071                                 fPos->GetUParameter(), fPos->GetVParameter() );
13072     break;
13073   }
13074   case SMDS_TOP_EDGE:
13075   {
13076     // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
13077     const SMDS_EdgePosition* ePos = static_cast< const SMDS_EdgePosition* >( pos );
13078     GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
13079     break;
13080   }
13081   case SMDS_TOP_VERTEX:
13082   {
13083     GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
13084     break;
13085   }
13086   case SMDS_TOP_UNSPEC:
13087   default:;
13088   }
13089 }
13090
13091 namespace // utils for MakePolyLine
13092 {
13093   //================================================================================
13094   /*!
13095    * \brief Sequence of found points and a current point data
13096    */
13097   struct Path
13098   {
13099     std::vector< gp_XYZ >   myPoints;
13100     double                  myLength;
13101
13102     int                     mySrcPntInd; //!< start point index
13103     const SMDS_MeshElement* myFace;
13104     SMESH_NodeXYZ           myNode1;
13105     SMESH_NodeXYZ           myNode2;
13106     int                     myNodeInd1;
13107     int                     myNodeInd2;
13108     double                  myDot1;
13109     double                  myDot2;
13110     TIDSortedElemSet        myElemSet, myAvoidSet;
13111
13112     Path(): myLength(0.0), myFace(0) {}
13113
13114     bool SetCutAtCorner( const SMESH_NodeXYZ&    cornerNode,
13115                          const SMDS_MeshElement* face,
13116                          const gp_XYZ&           plnNorm,
13117                          const gp_XYZ&           plnOrig );
13118
13119     void AddPoint( const gp_XYZ& p );
13120
13121     bool Extend( const gp_XYZ& plnNorm, const gp_XYZ& plnOrig );
13122
13123     bool ReachSamePoint( const Path& other );
13124
13125     static void Remove( std::vector< Path > & paths, size_t& i );
13126   };
13127
13128   //================================================================================
13129   /*!
13130    * \brief Return true if this Path meats another
13131    */
13132   //================================================================================
13133
13134   bool Path::ReachSamePoint( const Path& other )
13135   {
13136     return ( mySrcPntInd != other.mySrcPntInd &&
13137              myFace == other.myFace );
13138   }
13139
13140   //================================================================================
13141   /*!
13142    * \brief Remove a path from a vector
13143    */
13144   //================================================================================
13145
13146   void Path::Remove( std::vector< Path > & paths, size_t& i )
13147   {
13148     if ( paths.size() > 1 )
13149     {
13150       size_t j = paths.size() - 1; // last item to be removed
13151       if ( i < j )
13152       {
13153         paths[ i ].myPoints.swap( paths[ j ].myPoints );
13154         paths[ i ].myLength    = paths[ j ].myLength;
13155         paths[ i ].mySrcPntInd = paths[ j ].mySrcPntInd;
13156         paths[ i ].myFace      = paths[ j ].myFace;
13157         paths[ i ].myNode1     = paths[ j ].myNode1;
13158         paths[ i ].myNode2     = paths[ j ].myNode2;
13159         paths[ i ].myNodeInd1  = paths[ j ].myNodeInd1;
13160         paths[ i ].myNodeInd2  = paths[ j ].myNodeInd2;
13161         paths[ i ].myDot1      = paths[ j ].myDot1;
13162         paths[ i ].myDot2      = paths[ j ].myDot2;
13163       }
13164     }
13165     paths.pop_back();
13166     if ( i > 0 )
13167       --i;
13168   }
13169
13170   //================================================================================
13171   /*!
13172    * \brief Store a point that is at a node of a face if the face is intersected by plane.
13173    *        Return false if the node is a sole intersection point of the face and the plane
13174    */
13175   //================================================================================
13176
13177   bool Path::SetCutAtCorner( const SMESH_NodeXYZ&    cornerNode,
13178                              const SMDS_MeshElement* face,
13179                              const gp_XYZ&           plnNorm,
13180                              const gp_XYZ&           plnOrig )
13181   {
13182     if ( face == myFace )
13183       return false;
13184     myNodeInd1 = face->GetNodeIndex( cornerNode._node );
13185     myNodeInd2 = ( myNodeInd1 + 1 ) % face->NbCornerNodes();
13186     int   ind3 = ( myNodeInd1 + 2 ) % face->NbCornerNodes();
13187     myNode1.Set( face->GetNode( ind3 ));
13188     myNode2.Set( face->GetNode( myNodeInd2 ));
13189
13190     myDot1 = plnNorm * ( myNode1 - plnOrig );
13191     myDot2 = plnNorm * ( myNode2 - plnOrig );
13192
13193     bool ok = ( myDot1 * myDot2 < 0 );
13194     if ( !ok && myDot1 * myDot2 == 0 )
13195     {
13196       ok = ( myDot1 != myDot2 );
13197       if ( ok && myFace )
13198         ok = ( myFace->GetNodeIndex(( myDot1 == 0 ? myNode1 : myNode2 )._node ) < 0 );
13199     }
13200     if ( ok )
13201     {
13202       myFace = face;
13203       myDot1 = 0;
13204       AddPoint( cornerNode );
13205     }
13206     return ok;
13207   }
13208
13209   //================================================================================
13210   /*!
13211    * \brief Store a point and update myLength
13212    */
13213   //================================================================================
13214
13215   void Path::AddPoint( const gp_XYZ& p )
13216   {
13217     if ( !myPoints.empty() )
13218       myLength += ( p - myPoints.back() ).Modulus();
13219     else
13220       myLength = 0;
13221     myPoints.push_back( p );
13222   }
13223
13224   //================================================================================
13225   /*!
13226    * \brief Try to find the next point
13227    *  \param [in] plnNorm - cutting plane normal
13228    *  \param [in] plnOrig - cutting plane origin
13229    */
13230   //================================================================================
13231
13232   bool Path::Extend( const gp_XYZ& plnNorm, const gp_XYZ& plnOrig )
13233   {
13234     int nodeInd3 = ( myNodeInd1 + 1 ) % myFace->NbCornerNodes();
13235     if ( myNodeInd2 == nodeInd3 )
13236       nodeInd3 = ( myNodeInd1 + 2 ) % myFace->NbCornerNodes();
13237
13238     SMESH_NodeXYZ node3 = myFace->GetNode( nodeInd3 );
13239     double         dot3 = plnNorm * ( node3 - plnOrig );
13240
13241     if ( dot3 * myDot1 < 0. )
13242     {
13243       myNode2    = node3;
13244       myNodeInd2 = nodeInd3;
13245       myDot2     = dot3;
13246     }
13247     else if ( dot3 * myDot2 < 0. )
13248     {
13249       myNode1    = node3;
13250       myNodeInd1 = nodeInd3;
13251       myDot1     = dot3;
13252     }
13253     else if ( dot3 == 0. )
13254     {
13255       SMDS_ElemIteratorPtr fIt = node3._node->GetInverseElementIterator(SMDSAbs_Face);
13256       while ( fIt->more() )
13257         if ( SetCutAtCorner( node3, fIt->next(), plnNorm, plnOrig ))
13258           return true;
13259       return false;
13260     }
13261     else if ( myDot2 == 0. )
13262     {
13263       SMESH_NodeXYZ node2 = myNode2; // copy as myNode2 changes in SetCutAtCorner()
13264       SMDS_ElemIteratorPtr fIt = node2._node->GetInverseElementIterator(SMDSAbs_Face);
13265       while ( fIt->more() )
13266         if ( SetCutAtCorner( node2, fIt->next(), plnNorm, plnOrig ))
13267           return true;
13268       return false;
13269     }
13270
13271     double r = Abs( myDot1 / ( myDot2 - myDot1 ));
13272     AddPoint( myNode1 * ( 1 - r ) + myNode2 * r );
13273
13274     myAvoidSet.clear();
13275     myAvoidSet.insert( myFace );
13276     myFace = SMESH_MeshAlgos::FindFaceInSet( myNode1._node, myNode2._node,
13277                                              myElemSet,   myAvoidSet,
13278                                              &myNodeInd1, &myNodeInd2 );
13279     return myFace;
13280   }
13281
13282   //================================================================================
13283   /*!
13284    * \brief Compute a path between two points of PolySegment
13285    */
13286   struct PolyPathCompute
13287   {
13288     SMESH_MeshEditor::TListOfPolySegments& mySegments; //!< inout PolySegment's
13289     std::vector< Path >&                   myPaths;    //!< path of each of segments to compute
13290     SMESH_Mesh*                            myMesh;
13291     mutable std::vector< std::string >     myErrors;
13292
13293     PolyPathCompute( SMESH_MeshEditor::TListOfPolySegments& theSegments,
13294                      std::vector< Path >&                   thePaths,
13295                      SMESH_Mesh*                            theMesh):
13296       mySegments( theSegments ),
13297       myPaths( thePaths ),
13298       myMesh( theMesh ),
13299       myErrors( theSegments.size() )
13300     {
13301     }
13302 #undef SMESH_CAUGHT
13303 #define SMESH_CAUGHT myErrors[i] =
13304     void operator() ( const int i ) const
13305     {
13306       SMESH_TRY;
13307       const_cast< PolyPathCompute* >( this )->Compute( i );
13308       SMESH_CATCH( SMESH::returnError );
13309     }
13310 #undef SMESH_CAUGHT
13311     //================================================================================
13312     /*!
13313      * \brief Compute a path of a given segment
13314      */
13315     //================================================================================
13316
13317     void Compute( const int iSeg )
13318     {
13319       SMESH_MeshEditor::PolySegment& polySeg = mySegments[ iSeg ];
13320
13321       // get a cutting plane
13322
13323       gp_XYZ p1 = SMESH_NodeXYZ( polySeg.myNode1[0] );
13324       gp_XYZ p2 = SMESH_NodeXYZ( polySeg.myNode1[1] );
13325       if ( polySeg.myNode2[0] ) p1 = 0.5 * ( p1 + SMESH_NodeXYZ( polySeg.myNode2[0] ));
13326       if ( polySeg.myNode2[1] ) p2 = 0.5 * ( p2 + SMESH_NodeXYZ( polySeg.myNode2[1] ));
13327
13328       gp_XYZ plnNorm = ( p1 - p2 ) ^ polySeg.myVector.XYZ();
13329       gp_XYZ plnOrig = p2;
13330
13331       // find paths connecting the 2 end points of polySeg
13332
13333       std::vector< Path > paths; paths.reserve(10);
13334
13335       // initialize paths
13336
13337       for ( int iP = 0; iP < 2; ++iP ) // loop on the polySeg end points
13338       {
13339         Path path;
13340         path.mySrcPntInd = iP;
13341         size_t nbPaths = paths.size();
13342
13343         if ( polySeg.myNode2[ iP ] && polySeg.myNode2[ iP ] != polySeg.myNode1[ iP ] )
13344         {
13345           while (( path.myFace = SMESH_MeshAlgos::FindFaceInSet( polySeg.myNode1[ iP ],
13346                                                                  polySeg.myNode2[ iP ],
13347                                                                  path.myElemSet,
13348                                                                  path.myAvoidSet,
13349                                                                  &path.myNodeInd1,
13350                                                                  &path.myNodeInd2 )))
13351           {
13352             path.myNode1.Set( polySeg.myNode1[ iP ]);
13353             path.myNode2.Set( polySeg.myNode2[ iP ]);
13354             path.myDot1 = plnNorm * ( path.myNode1 - plnOrig );
13355             path.myDot2 = plnNorm * ( path.myNode2 - plnOrig );
13356             path.myPoints.clear();
13357             path.AddPoint( 0.5 * ( path.myNode1 + path.myNode2 ));
13358             path.myAvoidSet.insert( path.myFace );
13359             paths.push_back( path );
13360           }
13361           if ( nbPaths == paths.size() )
13362             throw SALOME_Exception ( SMESH_Comment("No face edge found by point ") << iP+1
13363                                      << " in a PolySegment " << iSeg );
13364         }
13365         else // an end point is at node
13366         {
13367           std::set<const SMDS_MeshNode* > nodes;
13368           SMDS_ElemIteratorPtr fIt = polySeg.myNode1[ iP ]->GetInverseElementIterator(SMDSAbs_Face);
13369           while ( fIt->more() )
13370           {
13371             path.myPoints.clear();
13372             if ( path.SetCutAtCorner( polySeg.myNode1[ iP ], fIt->next(), plnNorm, plnOrig ))
13373             {
13374               if (( path.myDot1 * path.myDot2 != 0 ) ||
13375                   ( nodes.insert( path.myDot1 == 0 ? path.myNode1._node : path.myNode2._node ).second ))
13376                 paths.push_back( path );
13377             }
13378           }
13379         }
13380
13381         // look for a one-segment path
13382         for ( size_t i = 0; i < nbPaths; ++i )
13383           for ( size_t j = nbPaths; j < paths.size(); ++j )
13384             if ( paths[i].myFace == paths[j].myFace )
13385             {
13386               myPaths[ iSeg ].myPoints.push_back( paths[i].myPoints[0] );
13387               myPaths[ iSeg ].myPoints.push_back( paths[j].myPoints[0] );
13388               paths.clear();
13389             }
13390       }
13391
13392       // extend paths
13393
13394       myPaths[ iSeg ].myLength = 1e100;
13395
13396       while ( paths.size() >= 2 )
13397       {
13398         for ( size_t i = 0; i < paths.size(); ++i )
13399         {
13400           Path& path = paths[ i ];
13401           if ( !path.Extend( plnNorm, plnOrig ) ||         // path reached a mesh boundary
13402                path.myLength > myPaths[ iSeg ].myLength ) // path is longer than others
13403           {
13404             Path::Remove( paths, i );
13405             continue;
13406           }
13407
13408           // join paths that reach same point
13409           for ( size_t j = 0; j < paths.size(); ++j )
13410           {
13411             if ( i != j && paths[i].ReachSamePoint( paths[j] ))
13412             {
13413               double   distLast = ( paths[i].myPoints.back() - paths[j].myPoints.back() ).Modulus();
13414               double fullLength = ( paths[i].myLength + paths[j].myLength + distLast );
13415               if ( fullLength < myPaths[ iSeg ].myLength )
13416               {
13417                 myPaths[ iSeg ].myLength = fullLength;
13418                 std::vector< gp_XYZ > & allPoints = myPaths[ iSeg ].myPoints;
13419                 allPoints.swap( paths[i].myPoints );
13420                 allPoints.insert( allPoints.end(),
13421                                   paths[j].myPoints.rbegin(),
13422                                   paths[j].myPoints.rend() );
13423               }
13424               Path::Remove( paths, i );
13425               Path::Remove( paths, j );
13426             }
13427           }
13428         }
13429         if ( !paths.empty() && (int) paths[0].myPoints.size() > myMesh->NbFaces() )
13430           throw SALOME_Exception(LOCALIZED( "Infinite loop in MakePolyLine()"));
13431       }
13432
13433       if ( myPaths[ iSeg ].myPoints.empty() )
13434         throw SALOME_Exception( SMESH_Comment("Can't find a full path for PolySegment #") << iSeg );
13435
13436     } // PolyPathCompute::Compute()
13437
13438   }; // struct PolyPathCompute
13439
13440 } // namespace
13441
13442 //=======================================================================
13443 //function : MakePolyLine
13444 //purpose  : Create a polyline consisting of 1D mesh elements each lying on a 2D element of
13445 //           the initial mesh
13446 //=======================================================================
13447
13448 void SMESH_MeshEditor::MakePolyLine( TListOfPolySegments&   theSegments,
13449                                      SMESHDS_Group*         theGroup,
13450                                      SMESH_ElementSearcher* theSearcher)
13451 {
13452   std::vector< Path > segPaths( theSegments.size() ); // path of each of segments
13453
13454   SMESH_ElementSearcher* searcher = theSearcher;
13455   SMESHUtils::Deleter<SMESH_ElementSearcher> delSearcher;
13456   if ( !searcher )
13457   {
13458     searcher = SMESH_MeshAlgos::GetElementSearcher( *GetMeshDS() );
13459     delSearcher._obj = searcher;
13460   }
13461
13462   // get cutting planes
13463
13464   std::vector< bool > isVectorOK( theSegments.size(), true );
13465   const double planarCoef = 0.333; // plane height in planar case
13466
13467   for ( size_t iSeg = 0; iSeg < theSegments.size(); ++iSeg )
13468   {
13469     PolySegment& polySeg = theSegments[ iSeg ];
13470
13471     gp_XYZ p1 = SMESH_NodeXYZ( polySeg.myNode1[0] );
13472     gp_XYZ p2 = SMESH_NodeXYZ( polySeg.myNode1[1] );
13473     if ( polySeg.myNode2[0] ) p1 = 0.5 * ( p1 + SMESH_NodeXYZ( polySeg.myNode2[0] ));
13474     if ( polySeg.myNode2[1] ) p2 = 0.5 * ( p2 + SMESH_NodeXYZ( polySeg.myNode2[1] ));
13475
13476     gp_XYZ plnNorm = ( p1 - p2 ) ^ polySeg.myVector.XYZ();
13477
13478     isVectorOK[ iSeg ] = ( plnNorm.Modulus() > std::numeric_limits<double>::min() );
13479     if ( !isVectorOK[ iSeg ])
13480     {
13481       gp_XYZ pMid = 0.5 * ( p1 + p2 );
13482       const SMDS_MeshElement* face;
13483       polySeg.myMidProjPoint = searcher->Project( pMid, SMDSAbs_Face, &face );
13484       polySeg.myVector       = polySeg.myMidProjPoint.XYZ() - pMid;
13485
13486       gp_XYZ faceNorm;
13487       SMESH_MeshAlgos::FaceNormal( face, faceNorm );
13488
13489       if ( polySeg.myVector.Magnitude() < Precision::Confusion() ||
13490            polySeg.myVector * faceNorm  < Precision::Confusion() )
13491       {
13492         polySeg.myVector = faceNorm;
13493         polySeg.myMidProjPoint = pMid + faceNorm * ( p1 - p2 ).Modulus() * planarCoef;
13494       }
13495     }
13496     else
13497     {
13498       polySeg.myVector = plnNorm ^ ( p1 - p2 );
13499     }
13500   }
13501
13502   // assure that inverse elements are constructed, avoid their concurrent building in threads
13503   GetMeshDS()->nodesIterator()->next()->NbInverseElements();
13504
13505   // find paths
13506
13507   PolyPathCompute algo( theSegments, segPaths, myMesh );
13508   OSD_Parallel::For( 0, theSegments.size(), algo, theSegments.size() == 1 );
13509
13510   for ( size_t iSeg = 0; iSeg < theSegments.size(); ++iSeg )
13511     if ( !algo.myErrors[ iSeg ].empty() )
13512       throw SALOME_Exception( algo.myErrors[ iSeg ].c_str() );
13513
13514   // create an 1D mesh
13515
13516   const SMDS_MeshNode *n, *nPrev = 0;
13517   SMESHDS_Mesh* mesh = GetMeshDS();
13518
13519   for ( size_t iSeg = 0; iSeg < theSegments.size(); ++iSeg )
13520   {
13521     const Path& path = segPaths[iSeg];
13522     if ( path.myPoints.size() < 2 )
13523       continue;
13524
13525     double tol = path.myLength / path.myPoints.size() / 1000.;
13526     if ( !nPrev || ( SMESH_NodeXYZ( nPrev ) - path.myPoints[0] ).SquareModulus() > tol*tol )
13527     {
13528       nPrev = mesh->AddNode( path.myPoints[0].X(), path.myPoints[0].Y(), path.myPoints[0].Z() );
13529       myLastCreatedNodes.push_back( nPrev );
13530     }
13531     for ( size_t iP = 1; iP < path.myPoints.size(); ++iP )
13532     {
13533       n = mesh->AddNode( path.myPoints[iP].X(), path.myPoints[iP].Y(), path.myPoints[iP].Z() );
13534       myLastCreatedNodes.push_back( n );
13535
13536       const SMDS_MeshElement* elem = mesh->AddEdge( nPrev, n );
13537       myLastCreatedElems.push_back( elem );
13538       if ( theGroup )
13539         theGroup->Add( elem );
13540
13541       nPrev = n;
13542     }
13543
13544     // return a vector
13545
13546     gp_XYZ pMid = 0.5 * ( path.myPoints[0] + path.myPoints.back() );
13547     if ( isVectorOK[ iSeg ])
13548     {
13549       // find the most distance point of a path
13550       double maxDist = 0;
13551       for ( size_t iP = 1; iP < path.myPoints.size(); ++iP )
13552       {
13553         double dist = Abs( theSegments[iSeg].myVector * ( path.myPoints[iP] - path.myPoints[0] ));
13554         if ( dist > maxDist )
13555         {
13556           maxDist = dist;
13557           theSegments[iSeg].myMidProjPoint = path.myPoints[iP];
13558         }
13559       }
13560       if ( maxDist < Precision::Confusion() ) // planar case
13561         theSegments[iSeg].myMidProjPoint =
13562           pMid + theSegments[iSeg].myVector.XYZ().Normalized() * path.myLength * planarCoef;
13563     }
13564     theSegments[iSeg].myVector = gp_Vec( pMid, theSegments[iSeg].myMidProjPoint );
13565   }
13566
13567   return;
13568 }