Salome HOME
GPUSPHGUI: add Offset transformation
[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   : SortableElement
7733 // purpose : allow sorting elements basing on their nodes
7734 // ========================================================
7735 class SortableElement : public set <const SMDS_MeshElement*>
7736 {
7737 public:
7738
7739   SortableElement( const SMDS_MeshElement* theElem )
7740   {
7741     myElem = theElem;
7742     SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
7743     while ( nodeIt->more() )
7744       this->insert( nodeIt->next() );
7745   }
7746
7747   const SMDS_MeshElement* Get() const
7748   { return myElem; }
7749
7750 private:
7751   mutable const SMDS_MeshElement* myElem;
7752 };
7753
7754 //=======================================================================
7755 //function : FindEqualElements
7756 //purpose  : Return list of group of elements built on the same nodes.
7757 //           Search among theElements or in the whole mesh if theElements is empty
7758 //=======================================================================
7759
7760 void SMESH_MeshEditor::FindEqualElements(TIDSortedElemSet &        theElements,
7761                                          TListOfListOfElementsID & theGroupsOfElementsID)
7762 {
7763   ClearLastCreated();
7764
7765   typedef map< SortableElement, int > TMapOfNodeSet;
7766   typedef list<int> TGroupOfElems;
7767
7768   SMDS_ElemIteratorPtr elemIt;
7769   if ( theElements.empty() ) elemIt = GetMeshDS()->elementsIterator();
7770   else                       elemIt = SMESHUtils::elemSetIterator( theElements );
7771
7772   vector< TGroupOfElems > arrayOfGroups;
7773   TGroupOfElems groupOfElems;
7774   TMapOfNodeSet mapOfNodeSet;
7775
7776   for ( int iGroup = 0; elemIt->more(); )
7777   {
7778     const SMDS_MeshElement* curElem = elemIt->next();
7779     SortableElement SE(curElem);
7780     // check uniqueness
7781     pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, iGroup));
7782     if ( !pp.second ) { // one more coincident elem
7783       TMapOfNodeSet::iterator& itSE = pp.first;
7784       int iG = itSE->second;
7785       arrayOfGroups[ iG ].push_back( curElem->GetID() );
7786     }
7787     else {
7788       arrayOfGroups.push_back( groupOfElems );
7789       arrayOfGroups.back().push_back( curElem->GetID() );
7790       iGroup++;
7791     }
7792   }
7793
7794   groupOfElems.clear();
7795   vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
7796   for ( ; groupIt != arrayOfGroups.end(); ++groupIt )
7797   {
7798     if ( groupIt->size() > 1 ) {
7799       //groupOfElems.sort(); -- theElements is sorted already
7800       theGroupsOfElementsID.push_back( groupOfElems );
7801       theGroupsOfElementsID.back().splice( theGroupsOfElementsID.back().end(), *groupIt );
7802     }
7803   }
7804 }
7805
7806 //=======================================================================
7807 //function : MergeElements
7808 //purpose  : In each given group, substitute all elements by the first one.
7809 //=======================================================================
7810
7811 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
7812 {
7813   ClearLastCreated();
7814
7815   typedef list<int> TListOfIDs;
7816   TListOfIDs rmElemIds; // IDs of elems to remove
7817
7818   SMESHDS_Mesh* aMesh = GetMeshDS();
7819
7820   TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
7821   while ( groupsIt != theGroupsOfElementsID.end() ) {
7822     TListOfIDs& aGroupOfElemID = *groupsIt;
7823     aGroupOfElemID.sort();
7824     int elemIDToKeep = aGroupOfElemID.front();
7825     const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
7826     aGroupOfElemID.pop_front();
7827     TListOfIDs::iterator idIt = aGroupOfElemID.begin();
7828     while ( idIt != aGroupOfElemID.end() ) {
7829       int elemIDToRemove = *idIt;
7830       const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
7831       // add the kept element in groups of removed one (PAL15188)
7832       AddToSameGroups( elemToKeep, elemToRemove, aMesh );
7833       rmElemIds.push_back( elemIDToRemove );
7834       ++idIt;
7835     }
7836     ++groupsIt;
7837   }
7838
7839   Remove( rmElemIds, false );
7840 }
7841
7842 //=======================================================================
7843 //function : MergeEqualElements
7844 //purpose  : Remove all but one of elements built on the same nodes.
7845 //=======================================================================
7846
7847 void SMESH_MeshEditor::MergeEqualElements()
7848 {
7849   TIDSortedElemSet aMeshElements; /* empty input ==
7850                                      to merge equal elements in the whole mesh */
7851   TListOfListOfElementsID aGroupsOfElementsID;
7852   FindEqualElements(aMeshElements, aGroupsOfElementsID);
7853   MergeElements(aGroupsOfElementsID);
7854 }
7855
7856 //=======================================================================
7857 //function : findAdjacentFace
7858 //purpose  :
7859 //=======================================================================
7860
7861 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
7862                                                 const SMDS_MeshNode* n2,
7863                                                 const SMDS_MeshElement* elem)
7864 {
7865   TIDSortedElemSet elemSet, avoidSet;
7866   if ( elem )
7867     avoidSet.insert ( elem );
7868   return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
7869 }
7870
7871 //=======================================================================
7872 //function : findSegment
7873 //purpose  : Return a mesh segment by two nodes one of which can be medium
7874 //=======================================================================
7875
7876 static const SMDS_MeshElement* findSegment(const SMDS_MeshNode* n1,
7877                                            const SMDS_MeshNode* n2)
7878 {
7879   SMDS_ElemIteratorPtr it = n1->GetInverseElementIterator( SMDSAbs_Edge );
7880   while ( it->more() )
7881   {
7882     const SMDS_MeshElement* seg = it->next();
7883     if ( seg->GetNodeIndex( n2 ) >= 0 )
7884       return seg;
7885   }
7886   return 0;
7887 }
7888
7889 //=======================================================================
7890 //function : FindFreeBorder
7891 //purpose  :
7892 //=======================================================================
7893
7894 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
7895
7896 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode*             theFirstNode,
7897                                        const SMDS_MeshNode*             theSecondNode,
7898                                        const SMDS_MeshNode*             theLastNode,
7899                                        list< const SMDS_MeshNode* > &   theNodes,
7900                                        list< const SMDS_MeshElement* >& theFaces)
7901 {
7902   if ( !theFirstNode || !theSecondNode )
7903     return false;
7904   // find border face between theFirstNode and theSecondNode
7905   const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
7906   if ( !curElem )
7907     return false;
7908
7909   theFaces.push_back( curElem );
7910   theNodes.push_back( theFirstNode );
7911   theNodes.push_back( theSecondNode );
7912
7913   const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
7914   TIDSortedElemSet foundElems;
7915   bool needTheLast = ( theLastNode != 0 );
7916
7917   while ( nStart != theLastNode ) {
7918     if ( nStart == theFirstNode )
7919       return !needTheLast;
7920
7921     // find all free border faces sharing form nStart
7922
7923     list< const SMDS_MeshElement* > curElemList;
7924     list< const SMDS_MeshNode* >    nStartList;
7925     SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
7926     while ( invElemIt->more() ) {
7927       const SMDS_MeshElement* e = invElemIt->next();
7928       if ( e == curElem || foundElems.insert( e ).second ) {
7929         // get nodes
7930         int iNode = 0, nbNodes = e->NbNodes();
7931         vector<const SMDS_MeshNode*> nodes(nbNodes+1);
7932
7933         if ( e->IsQuadratic() ) {
7934           const SMDS_VtkFace* F =
7935             dynamic_cast<const SMDS_VtkFace*>(e);
7936           if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
7937           // use special nodes iterator
7938           SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
7939           while( anIter->more() ) {
7940             nodes[ iNode++ ] = cast2Node(anIter->next());
7941           }
7942         }
7943         else {
7944           SMDS_ElemIteratorPtr nIt = e->nodesIterator();
7945           while ( nIt->more() )
7946             nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
7947         }
7948         nodes[ iNode ] = nodes[ 0 ];
7949         // check 2 links
7950         for ( iNode = 0; iNode < nbNodes; iNode++ )
7951           if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
7952                (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
7953               ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
7954           {
7955             nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
7956             curElemList.push_back( e );
7957           }
7958       }
7959     }
7960     // analyse the found
7961
7962     int nbNewBorders = curElemList.size();
7963     if ( nbNewBorders == 0 ) {
7964       // no free border furthermore
7965       return !needTheLast;
7966     }
7967     else if ( nbNewBorders == 1 ) {
7968       // one more element found
7969       nIgnore = nStart;
7970       nStart = nStartList.front();
7971       curElem = curElemList.front();
7972       theFaces.push_back( curElem );
7973       theNodes.push_back( nStart );
7974     }
7975     else {
7976       // several continuations found
7977       list< const SMDS_MeshElement* >::iterator curElemIt;
7978       list< const SMDS_MeshNode* >::iterator nStartIt;
7979       // check if one of them reached the last node
7980       if ( needTheLast ) {
7981         for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7982              curElemIt!= curElemList.end();
7983              curElemIt++, nStartIt++ )
7984           if ( *nStartIt == theLastNode ) {
7985             theFaces.push_back( *curElemIt );
7986             theNodes.push_back( *nStartIt );
7987             return true;
7988           }
7989       }
7990       // find the best free border by the continuations
7991       list<const SMDS_MeshNode*>    contNodes[ 2 ], *cNL;
7992       list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
7993       for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7994            curElemIt!= curElemList.end();
7995            curElemIt++, nStartIt++ )
7996       {
7997         cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
7998         cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
7999         // find one more free border
8000         if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
8001           cNL->clear();
8002           cFL->clear();
8003         }
8004         else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
8005           // choice: clear a worse one
8006           int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
8007           int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
8008           contNodes[ iWorse ].clear();
8009           contFaces[ iWorse ].clear();
8010         }
8011       }
8012       if ( contNodes[0].empty() && contNodes[1].empty() )
8013         return false;
8014
8015       // push_back the best free border
8016       cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
8017       cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8018       theNodes.pop_back(); // remove nIgnore
8019       theNodes.pop_back(); // remove nStart
8020       theFaces.pop_back(); // remove curElem
8021       list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
8022       list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
8023       for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
8024       for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
8025       return true;
8026
8027     } // several continuations found
8028   } // while ( nStart != theLastNode )
8029
8030   return true;
8031 }
8032
8033 //=======================================================================
8034 //function : CheckFreeBorderNodes
8035 //purpose  : Return true if the tree nodes are on a free border
8036 //=======================================================================
8037
8038 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8039                                             const SMDS_MeshNode* theNode2,
8040                                             const SMDS_MeshNode* theNode3)
8041 {
8042   list< const SMDS_MeshNode* > nodes;
8043   list< const SMDS_MeshElement* > faces;
8044   return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8045 }
8046
8047 //=======================================================================
8048 //function : SewFreeBorder
8049 //purpose  :
8050 //warning  : for border-to-side sewing theSideSecondNode is considered as
8051 //           the last side node and theSideThirdNode is not used
8052 //=======================================================================
8053
8054 SMESH_MeshEditor::Sew_Error
8055 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8056                                  const SMDS_MeshNode* theBordSecondNode,
8057                                  const SMDS_MeshNode* theBordLastNode,
8058                                  const SMDS_MeshNode* theSideFirstNode,
8059                                  const SMDS_MeshNode* theSideSecondNode,
8060                                  const SMDS_MeshNode* theSideThirdNode,
8061                                  const bool           theSideIsFreeBorder,
8062                                  const bool           toCreatePolygons,
8063                                  const bool           toCreatePolyedrs)
8064 {
8065   ClearLastCreated();
8066
8067   Sew_Error aResult = SEW_OK;
8068
8069   // ====================================
8070   //    find side nodes and elements
8071   // ====================================
8072
8073   list< const SMDS_MeshNode* >    nSide[ 2 ];
8074   list< const SMDS_MeshElement* > eSide[ 2 ];
8075   list< const SMDS_MeshNode* >::iterator    nIt[ 2 ];
8076   list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8077
8078   // Free border 1
8079   // --------------
8080   if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8081                       nSide[0], eSide[0])) {
8082     MESSAGE(" Free Border 1 not found " );
8083     aResult = SEW_BORDER1_NOT_FOUND;
8084   }
8085   if (theSideIsFreeBorder) {
8086     // Free border 2
8087     // --------------
8088     if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8089                         nSide[1], eSide[1])) {
8090       MESSAGE(" Free Border 2 not found " );
8091       aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8092     }
8093   }
8094   if ( aResult != SEW_OK )
8095     return aResult;
8096
8097   if (!theSideIsFreeBorder) {
8098     // Side 2
8099     // --------------
8100
8101     // -------------------------------------------------------------------------
8102     // Algo:
8103     // 1. If nodes to merge are not coincident, move nodes of the free border
8104     //    from the coord sys defined by the direction from the first to last
8105     //    nodes of the border to the correspondent sys of the side 2
8106     // 2. On the side 2, find the links most co-directed with the correspondent
8107     //    links of the free border
8108     // -------------------------------------------------------------------------
8109
8110     // 1. Since sewing may break if there are volumes to split on the side 2,
8111     //    we won't move nodes but just compute new coordinates for them
8112     typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8113     TNodeXYZMap nBordXYZ;
8114     list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8115     list< const SMDS_MeshNode* >::iterator nBordIt;
8116
8117     gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8118     gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8119     gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8120     gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8121     double tol2 = 1.e-8;
8122     gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8123     if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8124       // Need node movement.
8125
8126       // find X and Z axes to create trsf
8127       gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8128       gp_Vec X = Zs ^ Zb;
8129       if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8130         // Zb || Zs
8131         X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8132
8133       // coord systems
8134       gp_Ax3 toBordAx( Pb1, Zb, X );
8135       gp_Ax3 fromSideAx( Ps1, Zs, X );
8136       gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8137       // set trsf
8138       gp_Trsf toBordSys, fromSide2Sys;
8139       toBordSys.SetTransformation( toBordAx );
8140       fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8141       fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8142
8143       // move
8144       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8145         const SMDS_MeshNode* n = *nBordIt;
8146         gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8147         toBordSys.Transforms( xyz );
8148         fromSide2Sys.Transforms( xyz );
8149         nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8150       }
8151     }
8152     else {
8153       // just insert nodes XYZ in the nBordXYZ map
8154       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8155         const SMDS_MeshNode* n = *nBordIt;
8156         nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8157       }
8158     }
8159
8160     // 2. On the side 2, find the links most co-directed with the correspondent
8161     //    links of the free border
8162
8163     list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8164     list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8165     sideNodes.push_back( theSideFirstNode );
8166
8167     bool hasVolumes = false;
8168     LinkID_Gen aLinkID_Gen( GetMeshDS() );
8169     set<long> foundSideLinkIDs, checkedLinkIDs;
8170     SMDS_VolumeTool volume;
8171     //const SMDS_MeshNode* faceNodes[ 4 ];
8172
8173     const SMDS_MeshNode*    sideNode;
8174     const SMDS_MeshElement* sideElem  = 0;
8175     const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8176     const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8177     nBordIt = bordNodes.begin();
8178     nBordIt++;
8179     // border node position and border link direction to compare with
8180     gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8181     gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8182     // choose next side node by link direction or by closeness to
8183     // the current border node:
8184     bool searchByDir = ( *nBordIt != theBordLastNode );
8185     do {
8186       // find the next node on the Side 2
8187       sideNode = 0;
8188       double maxDot = -DBL_MAX, minDist = DBL_MAX;
8189       long linkID;
8190       checkedLinkIDs.clear();
8191       gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8192
8193       // loop on inverse elements of current node (prevSideNode) on the Side 2
8194       SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8195       while ( invElemIt->more() )
8196       {
8197         const SMDS_MeshElement* elem = invElemIt->next();
8198         // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8199         int iPrevNode = 0, iNode = 0, nbNodes = elem->NbNodes();
8200         vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8201         bool isVolume = volume.Set( elem );
8202         const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8203         if ( isVolume ) // --volume
8204           hasVolumes = true;
8205         else if ( elem->GetType()==SMDSAbs_Face ) { // --face
8206           // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8207           if(elem->IsQuadratic()) {
8208             const SMDS_VtkFace* F =
8209               dynamic_cast<const SMDS_VtkFace*>(elem);
8210             if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8211             // use special nodes iterator
8212             SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8213             while( anIter->more() ) {
8214               nodes[ iNode ] = cast2Node(anIter->next());
8215               if ( nodes[ iNode++ ] == prevSideNode )
8216                 iPrevNode = iNode - 1;
8217             }
8218           }
8219           else {
8220             SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
8221             while ( nIt->more() ) {
8222               nodes[ iNode ] = cast2Node( nIt->next() );
8223               if ( nodes[ iNode++ ] == prevSideNode )
8224                 iPrevNode = iNode - 1;
8225             }
8226           }
8227           // there are 2 links to check
8228           nbNodes = 2;
8229         }
8230         else // --edge
8231           continue;
8232         // loop on links, to be precise, on the second node of links
8233         for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8234           const SMDS_MeshNode* n = nodes[ iNode ];
8235           if ( isVolume ) {
8236             if ( !volume.IsLinked( n, prevSideNode ))
8237               continue;
8238           }
8239           else {
8240             if ( iNode ) // a node before prevSideNode
8241               n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8242             else         // a node after prevSideNode
8243               n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8244           }
8245           // check if this link was already used
8246           long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8247           bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8248           if (!isJustChecked &&
8249               foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8250           {
8251             // test a link geometrically
8252             gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8253             bool linkIsBetter = false;
8254             double dot = 0.0, dist = 0.0;
8255             if ( searchByDir ) { // choose most co-directed link
8256               dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8257               linkIsBetter = ( dot > maxDot );
8258             }
8259             else { // choose link with the node closest to bordPos
8260               dist = ( nextXYZ - bordPos ).SquareModulus();
8261               linkIsBetter = ( dist < minDist );
8262             }
8263             if ( linkIsBetter ) {
8264               maxDot = dot;
8265               minDist = dist;
8266               linkID = iLink;
8267               sideNode = n;
8268               sideElem = elem;
8269             }
8270           }
8271         }
8272       } // loop on inverse elements of prevSideNode
8273
8274       if ( !sideNode ) {
8275         MESSAGE(" Can't find path by links of the Side 2 ");
8276         return SEW_BAD_SIDE_NODES;
8277       }
8278       sideNodes.push_back( sideNode );
8279       sideElems.push_back( sideElem );
8280       foundSideLinkIDs.insert ( linkID );
8281       prevSideNode = sideNode;
8282
8283       if ( *nBordIt == theBordLastNode )
8284         searchByDir = false;
8285       else {
8286         // find the next border link to compare with
8287         gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8288         searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8289         // move to next border node if sideNode is before forward border node (bordPos)
8290         while ( *nBordIt != theBordLastNode && !searchByDir ) {
8291           prevBordNode = *nBordIt;
8292           nBordIt++;
8293           bordPos = nBordXYZ[ *nBordIt ];
8294           bordDir = bordPos - nBordXYZ[ prevBordNode ];
8295           searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8296         }
8297       }
8298     }
8299     while ( sideNode != theSideSecondNode );
8300
8301     if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8302       MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8303       return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8304     }
8305   } // end nodes search on the side 2
8306
8307   // ============================
8308   // sew the border to the side 2
8309   // ============================
8310
8311   int nbNodes[]  = { (int)nSide[0].size(), (int)nSide[1].size() };
8312   int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8313
8314   bool toMergeConformal = ( nbNodes[0] == nbNodes[1] );
8315   if ( toMergeConformal && toCreatePolygons )
8316   {
8317     // do not merge quadrangles if polygons are OK (IPAL0052824)
8318     eIt[0] = eSide[0].begin();
8319     eIt[1] = eSide[1].begin();
8320     bool allQuads[2] = { true, true };
8321     for ( int iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8322       for ( ; allQuads[iBord] && eIt[iBord] != eSide[iBord].end(); ++eIt[iBord] )
8323         allQuads[iBord] = ( (*eIt[iBord])->NbCornerNodes() == 4 );
8324     }
8325     toMergeConformal = ( !allQuads[0] && !allQuads[1] );
8326   }
8327
8328   TListOfListOfNodes nodeGroupsToMerge;
8329   if (( toMergeConformal ) ||
8330       ( theSideIsFreeBorder && !theSideThirdNode )) {
8331
8332     // all nodes are to be merged
8333
8334     for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8335          nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8336          nIt[0]++, nIt[1]++ )
8337     {
8338       nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8339       nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8340       nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8341     }
8342   }
8343   else {
8344
8345     // insert new nodes into the border and the side to get equal nb of segments
8346
8347     // get normalized parameters of nodes on the borders
8348     vector< double > param[ 2 ];
8349     param[0].resize( maxNbNodes );
8350     param[1].resize( maxNbNodes );
8351     int iNode, iBord;
8352     for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8353       list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8354       list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8355       const SMDS_MeshNode* nPrev = *nIt;
8356       double bordLength = 0;
8357       for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8358         const SMDS_MeshNode* nCur = *nIt;
8359         gp_XYZ segment (nCur->X() - nPrev->X(),
8360                         nCur->Y() - nPrev->Y(),
8361                         nCur->Z() - nPrev->Z());
8362         double segmentLen = segment.Modulus();
8363         bordLength += segmentLen;
8364         param[ iBord ][ iNode ] = bordLength;
8365         nPrev = nCur;
8366       }
8367       // normalize within [0,1]
8368       for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8369         param[ iBord ][ iNode ] /= bordLength;
8370       }
8371     }
8372
8373     // loop on border segments
8374     const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8375     int i[ 2 ] = { 0, 0 };
8376     nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8377     nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8378
8379     TElemOfNodeListMap insertMap;
8380     TElemOfNodeListMap::iterator insertMapIt;
8381     // insertMap is
8382     // key:   elem to insert nodes into
8383     // value: 2 nodes to insert between + nodes to be inserted
8384     do {
8385       bool next[ 2 ] = { false, false };
8386
8387       // find min adjacent segment length after sewing
8388       double nextParam = 10., prevParam = 0;
8389       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8390         if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8391           nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8392         if ( i[ iBord ] > 0 )
8393           prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8394       }
8395       double  minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8396       double  maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8397       double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8398
8399       // choose to insert or to merge nodes
8400       double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8401       if ( Abs( du ) <= minSegLen * 0.2 ) {
8402         // merge
8403         // ------
8404         nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8405         const SMDS_MeshNode* n0 = *nIt[0];
8406         const SMDS_MeshNode* n1 = *nIt[1];
8407         nodeGroupsToMerge.back().push_back( n1 );
8408         nodeGroupsToMerge.back().push_back( n0 );
8409         // position of node of the border changes due to merge
8410         param[ 0 ][ i[0] ] += du;
8411         // move n1 for the sake of elem shape evaluation during insertion.
8412         // n1 will be removed by MergeNodes() anyway
8413         const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8414         next[0] = next[1] = true;
8415       }
8416       else {
8417         // insert
8418         // ------
8419         int intoBord = ( du < 0 ) ? 0 : 1;
8420         const SMDS_MeshElement* elem = *eIt [ intoBord ];
8421         const SMDS_MeshNode*    n1   = nPrev[ intoBord ];
8422         const SMDS_MeshNode*    n2   = *nIt [ intoBord ];
8423         const SMDS_MeshNode*    nIns = *nIt [ 1 - intoBord ];
8424         if ( intoBord == 1 ) {
8425           // move node of the border to be on a link of elem of the side
8426           gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8427           gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8428           double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8429           gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8430           GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8431         }
8432         insertMapIt = insertMap.find( elem );
8433         bool  notFound = ( insertMapIt == insertMap.end() );
8434         bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8435         if ( otherLink ) {
8436           // insert into another link of the same element:
8437           // 1. perform insertion into the other link of the elem
8438           list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8439           const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8440           const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8441           InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8442           // 2. perform insertion into the link of adjacent faces
8443           while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem )) {
8444             InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8445           }
8446           while ( const SMDS_MeshElement* seg = findSegment( n12, n22 )) {
8447             InsertNodesIntoLink( seg, n12, n22, nodeList );
8448           }
8449           if (toCreatePolyedrs) {
8450             // perform insertion into the links of adjacent volumes
8451             UpdateVolumes(n12, n22, nodeList);
8452           }
8453           // 3. find an element appeared on n1 and n2 after the insertion
8454           insertMap.erase( elem );
8455           elem = findAdjacentFace( n1, n2, 0 );
8456         }
8457         if ( notFound || otherLink ) {
8458           // add element and nodes of the side into the insertMap
8459           insertMapIt = insertMap.insert( make_pair( elem, list<const SMDS_MeshNode*>() )).first;
8460           (*insertMapIt).second.push_back( n1 );
8461           (*insertMapIt).second.push_back( n2 );
8462         }
8463         // add node to be inserted into elem
8464         (*insertMapIt).second.push_back( nIns );
8465         next[ 1 - intoBord ] = true;
8466       }
8467
8468       // go to the next segment
8469       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8470         if ( next[ iBord ] ) {
8471           if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8472             eIt[ iBord ]++;
8473           nPrev[ iBord ] = *nIt[ iBord ];
8474           nIt[ iBord ]++; i[ iBord ]++;
8475         }
8476       }
8477     }
8478     while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8479
8480     // perform insertion of nodes into elements
8481
8482     for (insertMapIt = insertMap.begin();
8483          insertMapIt != insertMap.end();
8484          insertMapIt++ )
8485     {
8486       const SMDS_MeshElement* elem = (*insertMapIt).first;
8487       list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8488       const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8489       const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8490
8491       InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8492
8493       while ( const SMDS_MeshElement* seg = findSegment( n1, n2 )) {
8494         InsertNodesIntoLink( seg, n1, n2, nodeList );
8495       }
8496
8497       if ( !theSideIsFreeBorder ) {
8498         // look for and insert nodes into the faces adjacent to elem
8499         while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem )) {
8500           InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8501         }
8502       }
8503       if (toCreatePolyedrs) {
8504         // perform insertion into the links of adjacent volumes
8505         UpdateVolumes(n1, n2, nodeList);
8506       }
8507     }
8508   } // end: insert new nodes
8509
8510   MergeNodes ( nodeGroupsToMerge );
8511
8512
8513   // Remove coincident segments
8514
8515   // get new segments
8516   TIDSortedElemSet segments;
8517   SMESH_SequenceOfElemPtr newFaces;
8518   for ( size_t i = 0; i < myLastCreatedElems.size(); ++i )
8519   {
8520     if ( !myLastCreatedElems[i] ) continue;
8521     if ( myLastCreatedElems[i]->GetType() == SMDSAbs_Edge )
8522       segments.insert( segments.end(), myLastCreatedElems[i] );
8523     else
8524       newFaces.push_back( myLastCreatedElems[i] );
8525   }
8526   // get segments adjacent to merged nodes
8527   TListOfListOfNodes::iterator groupIt = nodeGroupsToMerge.begin();
8528   for ( ; groupIt != nodeGroupsToMerge.end(); groupIt++ )
8529   {
8530     const list<const SMDS_MeshNode*>& nodes = *groupIt;
8531     SMDS_ElemIteratorPtr segIt = nodes.front()->GetInverseElementIterator( SMDSAbs_Edge );
8532     while ( segIt->more() )
8533       segments.insert( segIt->next() );
8534   }
8535
8536   // find coincident
8537   TListOfListOfElementsID equalGroups;
8538   if ( !segments.empty() )
8539     FindEqualElements( segments, equalGroups );
8540   if ( !equalGroups.empty() )
8541   {
8542     // remove from segments those that will be removed
8543     TListOfListOfElementsID::iterator itGroups = equalGroups.begin();
8544     for ( ; itGroups != equalGroups.end(); ++itGroups )
8545     {
8546       list< int >& group = *itGroups;
8547       list< int >::iterator id = group.begin();
8548       for ( ++id; id != group.end(); ++id )
8549         if ( const SMDS_MeshElement* seg = GetMeshDS()->FindElement( *id ))
8550           segments.erase( seg );
8551     }
8552     // remove equal segments
8553     MergeElements( equalGroups );
8554
8555     // restore myLastCreatedElems
8556     myLastCreatedElems = newFaces;
8557     TIDSortedElemSet::iterator seg = segments.begin();
8558     for ( ; seg != segments.end(); ++seg )
8559       myLastCreatedElems.push_back( *seg );
8560   }
8561
8562   return aResult;
8563 }
8564
8565 //=======================================================================
8566 //function : InsertNodesIntoLink
8567 //purpose  : insert theNodesToInsert into theElement between theBetweenNode1
8568 //           and theBetweenNode2 and split theElement
8569 //=======================================================================
8570
8571 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement*     theElement,
8572                                            const SMDS_MeshNode*        theBetweenNode1,
8573                                            const SMDS_MeshNode*        theBetweenNode2,
8574                                            list<const SMDS_MeshNode*>& theNodesToInsert,
8575                                            const bool                  toCreatePoly)
8576 {
8577   if ( !theElement ) return;
8578
8579   SMESHDS_Mesh *aMesh = GetMeshDS();
8580   vector<const SMDS_MeshElement*> newElems;
8581
8582   if ( theElement->GetType() == SMDSAbs_Edge )
8583   {
8584     theNodesToInsert.push_front( theBetweenNode1 );
8585     theNodesToInsert.push_back ( theBetweenNode2 );
8586     list<const SMDS_MeshNode*>::iterator n = theNodesToInsert.begin();
8587     const SMDS_MeshNode* n1 = *n;
8588     for ( ++n; n != theNodesToInsert.end(); ++n )
8589     {
8590       const SMDS_MeshNode* n2 = *n;
8591       if ( const SMDS_MeshElement* seg = aMesh->FindEdge( n1, n2 ))
8592         AddToSameGroups( seg, theElement, aMesh );
8593       else
8594         newElems.push_back( aMesh->AddEdge ( n1, n2 ));
8595       n1 = n2;
8596     }
8597     theNodesToInsert.pop_front();
8598     theNodesToInsert.pop_back();
8599
8600     if ( theElement->IsQuadratic() ) // add a not split part
8601     {
8602       vector<const SMDS_MeshNode*> nodes( theElement->begin_nodes(),
8603                                           theElement->end_nodes() );
8604       int iOther = 0, nbN = nodes.size();
8605       for ( ; iOther < nbN; ++iOther )
8606         if ( nodes[iOther] != theBetweenNode1 &&
8607              nodes[iOther] != theBetweenNode2 )
8608           break;
8609       if      ( iOther == 0 )
8610       {
8611         if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[0], nodes[1] ))
8612           AddToSameGroups( seg, theElement, aMesh );
8613         else
8614           newElems.push_back( aMesh->AddEdge ( nodes[0], nodes[1] ));
8615       }
8616       else if ( iOther == 2 )
8617       {
8618         if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[1], nodes[2] ))
8619           AddToSameGroups( seg, theElement, aMesh );
8620         else
8621           newElems.push_back( aMesh->AddEdge ( nodes[1], nodes[2] ));
8622       }
8623     }
8624     // treat new elements
8625     for ( size_t i = 0; i < newElems.size(); ++i )
8626       if ( newElems[i] )
8627       {
8628         aMesh->SetMeshElementOnShape( newElems[i], theElement->getshapeId() );
8629         myLastCreatedElems.push_back( newElems[i] );
8630       }
8631     ReplaceElemInGroups( theElement, newElems, aMesh );
8632     aMesh->RemoveElement( theElement );
8633     return;
8634
8635   } // if ( theElement->GetType() == SMDSAbs_Edge )
8636
8637   const SMDS_MeshElement* theFace = theElement;
8638   if ( theFace->GetType() != SMDSAbs_Face ) return;
8639
8640   // find indices of 2 link nodes and of the rest nodes
8641   int iNode = 0, il1, il2, i3, i4;
8642   il1 = il2 = i3 = i4 = -1;
8643   vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8644
8645   SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8646   while ( nodeIt->more() ) {
8647     const SMDS_MeshNode* n = nodeIt->next();
8648     if ( n == theBetweenNode1 )
8649       il1 = iNode;
8650     else if ( n == theBetweenNode2 )
8651       il2 = iNode;
8652     else if ( i3 < 0 )
8653       i3 = iNode;
8654     else
8655       i4 = iNode;
8656     nodes[ iNode++ ] = n;
8657   }
8658   if ( il1 < 0 || il2 < 0 || i3 < 0 )
8659     return ;
8660
8661   // arrange link nodes to go one after another regarding the face orientation
8662   bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8663   list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8664   if ( reverse ) {
8665     iNode = il1;
8666     il1 = il2;
8667     il2 = iNode;
8668     aNodesToInsert.reverse();
8669   }
8670   // check that not link nodes of a quadrangles are in good order
8671   int nbFaceNodes = theFace->NbNodes();
8672   if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8673     iNode = i3;
8674     i3 = i4;
8675     i4 = iNode;
8676   }
8677
8678   if (toCreatePoly || theFace->IsPoly()) {
8679
8680     iNode = 0;
8681     vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8682
8683     // add nodes of face up to first node of link
8684     bool isFLN = false;
8685
8686     if ( theFace->IsQuadratic() ) {
8687       const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>(theFace);
8688       if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8689       // use special nodes iterator
8690       SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8691       while( anIter->more()  && !isFLN ) {
8692         const SMDS_MeshNode* n = cast2Node(anIter->next());
8693         poly_nodes[iNode++] = n;
8694         if (n == nodes[il1]) {
8695           isFLN = true;
8696         }
8697       }
8698       // add nodes to insert
8699       list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8700       for (; nIt != aNodesToInsert.end(); nIt++) {
8701         poly_nodes[iNode++] = *nIt;
8702       }
8703       // add nodes of face starting from last node of link
8704       while ( anIter->more() ) {
8705         poly_nodes[iNode++] = cast2Node(anIter->next());
8706       }
8707     }
8708     else {
8709       SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8710       while ( nodeIt->more() && !isFLN ) {
8711         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8712         poly_nodes[iNode++] = n;
8713         if (n == nodes[il1]) {
8714           isFLN = true;
8715         }
8716       }
8717       // add nodes to insert
8718       list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8719       for (; nIt != aNodesToInsert.end(); nIt++) {
8720         poly_nodes[iNode++] = *nIt;
8721       }
8722       // add nodes of face starting from last node of link
8723       while ( nodeIt->more() ) {
8724         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8725         poly_nodes[iNode++] = n;
8726       }
8727     }
8728
8729     // make a new face
8730     newElems.push_back( aMesh->AddPolygonalFace( poly_nodes ));
8731   }
8732
8733   else if ( !theFace->IsQuadratic() )
8734   {
8735     // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8736     int nbLinkNodes = 2 + aNodesToInsert.size();
8737     //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8738     vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8739     linkNodes[ 0 ] = nodes[ il1 ];
8740     linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8741     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8742     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8743       linkNodes[ iNode++ ] = *nIt;
8744     }
8745     // decide how to split a quadrangle: compare possible variants
8746     // and choose which of splits to be a quadrangle
8747     int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad = 0;
8748     if ( nbFaceNodes == 3 ) {
8749       iBestQuad = nbSplits;
8750       i4 = i3;
8751     }
8752     else if ( nbFaceNodes == 4 ) {
8753       SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8754       double aBestRate = DBL_MAX;
8755       for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8756         i1 = 0; i2 = 1;
8757         double aBadRate = 0;
8758         // evaluate elements quality
8759         for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8760           if ( iSplit == iQuad ) {
8761             SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8762                                    linkNodes[ i2++ ],
8763                                    nodes[ i3 ],
8764                                    nodes[ i4 ]);
8765             aBadRate += getBadRate( &quad, aCrit );
8766           }
8767           else {
8768             SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8769                                    linkNodes[ i2++ ],
8770                                    nodes[ iSplit < iQuad ? i4 : i3 ]);
8771             aBadRate += getBadRate( &tria, aCrit );
8772           }
8773         }
8774         // choice
8775         if ( aBadRate < aBestRate ) {
8776           iBestQuad = iQuad;
8777           aBestRate = aBadRate;
8778         }
8779       }
8780     }
8781
8782     // create new elements
8783     i1 = 0; i2 = 1;
8784     for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ )
8785     {
8786       if ( iSplit == iBestQuad )
8787         newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8788                                             linkNodes[ i2++ ],
8789                                             nodes[ i3 ],
8790                                             nodes[ i4 ]));
8791       else
8792         newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8793                                             linkNodes[ i2++ ],
8794                                             nodes[ iSplit < iBestQuad ? i4 : i3 ]));
8795     }
8796
8797     const SMDS_MeshNode* newNodes[ 4 ];
8798     newNodes[ 0 ] = linkNodes[ i1 ];
8799     newNodes[ 1 ] = linkNodes[ i2 ];
8800     newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
8801     newNodes[ 3 ] = nodes[ i4 ];
8802     if (iSplit == iBestQuad)
8803       newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] ));
8804     else
8805       newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] ));
8806
8807   } // end if(!theFace->IsQuadratic())
8808
8809   else { // theFace is quadratic
8810     // we have to split theFace on simple triangles and one simple quadrangle
8811     int tmp = il1/2;
8812     int nbshift = tmp*2;
8813     // shift nodes in nodes[] by nbshift
8814     int i,j;
8815     for(i=0; i<nbshift; i++) {
8816       const SMDS_MeshNode* n = nodes[0];
8817       for(j=0; j<nbFaceNodes-1; j++) {
8818         nodes[j] = nodes[j+1];
8819       }
8820       nodes[nbFaceNodes-1] = n;
8821     }
8822     il1 = il1 - nbshift;
8823     // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
8824     //   n0      n1     n2    n0      n1     n2
8825     //     +-----+-----+        +-----+-----+
8826     //      \         /         |           |
8827     //       \       /          |           |
8828     //      n5+     +n3       n7+           +n3
8829     //         \   /            |           |
8830     //          \ /             |           |
8831     //           +              +-----+-----+
8832     //           n4           n6      n5     n4
8833
8834     // create new elements
8835     int n1,n2,n3;
8836     if ( nbFaceNodes == 6 ) { // quadratic triangle
8837       newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8838       if ( theFace->IsMediumNode(nodes[il1]) ) {
8839         // create quadrangle
8840         newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[5] ));
8841         n1 = 1;
8842         n2 = 2;
8843         n3 = 3;
8844       }
8845       else {
8846         // create quadrangle
8847         newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[5] ));
8848         n1 = 0;
8849         n2 = 1;
8850         n3 = 5;
8851       }
8852     }
8853     else { // nbFaceNodes==8 - quadratic quadrangle
8854       newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8855       newElems.push_back( aMesh->AddFace( nodes[5], nodes[6], nodes[7] ));
8856       newElems.push_back( aMesh->AddFace( nodes[5], nodes[7], nodes[3] ));
8857       if ( theFace->IsMediumNode( nodes[ il1 ])) {
8858         // create quadrangle
8859         newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[7] ));
8860         n1 = 1;
8861         n2 = 2;
8862         n3 = 3;
8863       }
8864       else {
8865         // create quadrangle
8866         newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[7] ));
8867         n1 = 0;
8868         n2 = 1;
8869         n3 = 7;
8870       }
8871     }
8872     // create needed triangles using n1,n2,n3 and inserted nodes
8873     int nbn = 2 + aNodesToInsert.size();
8874     vector<const SMDS_MeshNode*> aNodes(nbn);
8875     aNodes[0    ] = nodes[n1];
8876     aNodes[nbn-1] = nodes[n2];
8877     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8878     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8879       aNodes[iNode++] = *nIt;
8880     }
8881     for ( i = 1; i < nbn; i++ )
8882       newElems.push_back( aMesh->AddFace( aNodes[i-1], aNodes[i], nodes[n3] ));
8883   }
8884
8885   // remove the old face
8886   for ( size_t i = 0; i < newElems.size(); ++i )
8887     if ( newElems[i] )
8888     {
8889       aMesh->SetMeshElementOnShape( newElems[i], theFace->getshapeId() );
8890       myLastCreatedElems.push_back( newElems[i] );
8891     }
8892   ReplaceElemInGroups( theFace, newElems, aMesh );
8893   aMesh->RemoveElement(theFace);
8894
8895 } // InsertNodesIntoLink()
8896
8897 //=======================================================================
8898 //function : UpdateVolumes
8899 //purpose  :
8900 //=======================================================================
8901
8902 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode*        theBetweenNode1,
8903                                       const SMDS_MeshNode*        theBetweenNode2,
8904                                       list<const SMDS_MeshNode*>& theNodesToInsert)
8905 {
8906   ClearLastCreated();
8907
8908   SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
8909   while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
8910     const SMDS_MeshElement* elem = invElemIt->next();
8911
8912     // check, if current volume has link theBetweenNode1 - theBetweenNode2
8913     SMDS_VolumeTool aVolume (elem);
8914     if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
8915       continue;
8916
8917     // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
8918     int iface, nbFaces = aVolume.NbFaces();
8919     vector<const SMDS_MeshNode *> poly_nodes;
8920     vector<int> quantities (nbFaces);
8921
8922     for (iface = 0; iface < nbFaces; iface++) {
8923       int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
8924       // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
8925       const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
8926
8927       for (int inode = 0; inode < nbFaceNodes; inode++) {
8928         poly_nodes.push_back(faceNodes[inode]);
8929
8930         if (nbInserted == 0) {
8931           if (faceNodes[inode] == theBetweenNode1) {
8932             if (faceNodes[inode + 1] == theBetweenNode2) {
8933               nbInserted = theNodesToInsert.size();
8934
8935               // add nodes to insert
8936               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
8937               for (; nIt != theNodesToInsert.end(); nIt++) {
8938                 poly_nodes.push_back(*nIt);
8939               }
8940             }
8941           }
8942           else if (faceNodes[inode] == theBetweenNode2) {
8943             if (faceNodes[inode + 1] == theBetweenNode1) {
8944               nbInserted = theNodesToInsert.size();
8945
8946               // add nodes to insert in reversed order
8947               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
8948               nIt--;
8949               for (; nIt != theNodesToInsert.begin(); nIt--) {
8950                 poly_nodes.push_back(*nIt);
8951               }
8952               poly_nodes.push_back(*nIt);
8953             }
8954           }
8955           else {
8956           }
8957         }
8958       }
8959       quantities[iface] = nbFaceNodes + nbInserted;
8960     }
8961
8962     // Replace the volume
8963     SMESHDS_Mesh *aMesh = GetMeshDS();
8964
8965     if ( SMDS_MeshElement* newElem = aMesh->AddPolyhedralVolume( poly_nodes, quantities ))
8966     {
8967       aMesh->SetMeshElementOnShape( newElem, elem->getshapeId() );
8968       myLastCreatedElems.push_back( newElem );
8969       ReplaceElemInGroups( elem, newElem, aMesh );
8970     }
8971     aMesh->RemoveElement( elem );
8972   }
8973 }
8974
8975 namespace
8976 {
8977   //================================================================================
8978   /*!
8979    * \brief Transform any volume into data of SMDSEntity_Polyhedra
8980    */
8981   //================================================================================
8982
8983   void volumeToPolyhedron( const SMDS_MeshElement*         elem,
8984                            vector<const SMDS_MeshNode *> & nodes,
8985                            vector<int> &                   nbNodeInFaces )
8986   {
8987     nodes.clear();
8988     nbNodeInFaces.clear();
8989     SMDS_VolumeTool vTool ( elem );
8990     for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
8991     {
8992       const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
8993       nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
8994       nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
8995     }
8996   }
8997 }
8998
8999 //=======================================================================
9000 /*!
9001  * \brief Convert elements contained in a sub-mesh to quadratic
9002  * \return int - nb of checked elements
9003  */
9004 //=======================================================================
9005
9006 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh *   theSm,
9007                                              SMESH_MesherHelper& theHelper,
9008                                              const bool          theForce3d)
9009 {
9010   //MESSAGE("convertElemToQuadratic");
9011   int nbElem = 0;
9012   if( !theSm ) return nbElem;
9013
9014   vector<int> nbNodeInFaces;
9015   vector<const SMDS_MeshNode *> nodes;
9016   SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
9017   while(ElemItr->more())
9018   {
9019     nbElem++;
9020     const SMDS_MeshElement* elem = ElemItr->next();
9021     if( !elem ) continue;
9022
9023     // analyse a necessity of conversion
9024     const SMDSAbs_ElementType aType = elem->GetType();
9025     if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
9026       continue;
9027     const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
9028     bool hasCentralNodes = false;
9029     if ( elem->IsQuadratic() )
9030     {
9031       bool alreadyOK;
9032       switch ( aGeomType ) {
9033       case SMDSEntity_Quad_Triangle:
9034       case SMDSEntity_Quad_Quadrangle:
9035       case SMDSEntity_Quad_Hexa:
9036       case SMDSEntity_Quad_Penta:
9037         alreadyOK = !theHelper.GetIsBiQuadratic(); break;
9038
9039       case SMDSEntity_BiQuad_Triangle:
9040       case SMDSEntity_BiQuad_Quadrangle:
9041       case SMDSEntity_TriQuad_Hexa:
9042       case SMDSEntity_BiQuad_Penta:
9043         alreadyOK = theHelper.GetIsBiQuadratic();
9044         hasCentralNodes = true;
9045         break;
9046       default:
9047         alreadyOK = true;
9048       }
9049       // take into account already present medium nodes
9050       switch ( aType ) {
9051       case SMDSAbs_Volume:
9052         theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
9053       case SMDSAbs_Face:
9054         theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
9055       case SMDSAbs_Edge:
9056         theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
9057       default:;
9058       }
9059       if ( alreadyOK )
9060         continue;
9061     }
9062     // get elem data needed to re-create it
9063     //
9064     const int id      = elem->GetID();
9065     const int nbNodes = elem->NbCornerNodes();
9066     nodes.assign(elem->begin_nodes(), elem->end_nodes());
9067     if ( aGeomType == SMDSEntity_Polyhedra )
9068       nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
9069     else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
9070       volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9071
9072     // remove a linear element
9073     GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9074
9075     // remove central nodes of biquadratic elements (biquad->quad conversion)
9076     if ( hasCentralNodes )
9077       for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
9078         if ( nodes[i]->NbInverseElements() == 0 )
9079           GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
9080
9081     const SMDS_MeshElement* NewElem = 0;
9082
9083     switch( aType )
9084     {
9085     case SMDSAbs_Edge :
9086     {
9087       NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9088       break;
9089     }
9090     case SMDSAbs_Face :
9091     {
9092       switch(nbNodes)
9093       {
9094       case 3:
9095         NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9096         break;
9097       case 4:
9098         NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9099         break;
9100       default:
9101         NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9102       }
9103       break;
9104     }
9105     case SMDSAbs_Volume :
9106     {
9107       switch( aGeomType )
9108       {
9109       case SMDSEntity_Tetra:
9110         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9111         break;
9112       case SMDSEntity_Pyramid:
9113         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9114         break;
9115       case SMDSEntity_Penta:
9116       case SMDSEntity_Quad_Penta:
9117       case SMDSEntity_BiQuad_Penta:
9118         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9119         break;
9120       case SMDSEntity_Hexa:
9121       case SMDSEntity_Quad_Hexa:
9122       case SMDSEntity_TriQuad_Hexa:
9123         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9124                                       nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9125         break;
9126       case SMDSEntity_Hexagonal_Prism:
9127       default:
9128         NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9129       }
9130       break;
9131     }
9132     default :
9133       continue;
9134     }
9135     ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9136     if( NewElem && NewElem->getshapeId() < 1 )
9137       theSm->AddElement( NewElem );
9138   }
9139   return nbElem;
9140 }
9141 //=======================================================================
9142 //function : ConvertToQuadratic
9143 //purpose  :
9144 //=======================================================================
9145
9146 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
9147 {
9148   //MESSAGE("ConvertToQuadratic "<< theForce3d << " " << theToBiQuad);
9149   SMESHDS_Mesh* meshDS = GetMeshDS();
9150
9151   SMESH_MesherHelper aHelper(*myMesh);
9152
9153   aHelper.SetIsQuadratic( true );
9154   aHelper.SetIsBiQuadratic( theToBiQuad );
9155   aHelper.SetElementsOnShape(true);
9156   aHelper.ToFixNodeParameters( true );
9157
9158   // convert elements assigned to sub-meshes
9159   int nbCheckedElems = 0;
9160   if ( myMesh->HasShapeToMesh() )
9161   {
9162     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9163     {
9164       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9165       while ( smIt->more() ) {
9166         SMESH_subMesh* sm = smIt->next();
9167         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9168           aHelper.SetSubShape( sm->GetSubShape() );
9169           nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9170         }
9171       }
9172     }
9173   }
9174
9175   // convert elements NOT assigned to sub-meshes
9176   int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9177   if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
9178   {
9179     aHelper.SetElementsOnShape(false);
9180     SMESHDS_SubMesh *smDS = 0;
9181
9182     // convert edges
9183     SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9184     while( aEdgeItr->more() )
9185     {
9186       const SMDS_MeshEdge* edge = aEdgeItr->next();
9187       if ( !edge->IsQuadratic() )
9188       {
9189         int                  id = edge->GetID();
9190         const SMDS_MeshNode* n1 = edge->GetNode(0);
9191         const SMDS_MeshNode* n2 = edge->GetNode(1);
9192
9193         meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9194
9195         const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9196         ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9197       }
9198       else
9199       {
9200         aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
9201       }
9202     }
9203
9204     // convert faces
9205     SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9206     while( aFaceItr->more() )
9207     {
9208       const SMDS_MeshFace* face = aFaceItr->next();
9209       if ( !face ) continue;
9210       
9211       const SMDSAbs_EntityType type = face->GetEntityType();
9212       bool alreadyOK;
9213       switch( type )
9214       {
9215       case SMDSEntity_Quad_Triangle:
9216       case SMDSEntity_Quad_Quadrangle:
9217         alreadyOK = !theToBiQuad;
9218         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9219         break;
9220       case SMDSEntity_BiQuad_Triangle:
9221       case SMDSEntity_BiQuad_Quadrangle:
9222         alreadyOK = theToBiQuad;
9223         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9224         break;
9225       default: alreadyOK = false;
9226       }
9227       if ( alreadyOK )
9228         continue;
9229
9230       const int id = face->GetID();
9231       vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9232
9233       meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9234
9235       SMDS_MeshFace * NewFace = 0;
9236       switch( type )
9237       {
9238       case SMDSEntity_Triangle:
9239       case SMDSEntity_Quad_Triangle:
9240       case SMDSEntity_BiQuad_Triangle:
9241         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9242         if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
9243           GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
9244         break;
9245
9246       case SMDSEntity_Quadrangle:
9247       case SMDSEntity_Quad_Quadrangle:
9248       case SMDSEntity_BiQuad_Quadrangle:
9249         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9250         if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
9251           GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
9252         break;
9253
9254       default:;
9255         NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9256       }
9257       ReplaceElemInGroups( face, NewFace, GetMeshDS());
9258     }
9259
9260     // convert volumes
9261     vector<int> nbNodeInFaces;
9262     SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9263     while(aVolumeItr->more())
9264     {
9265       const SMDS_MeshVolume* volume = aVolumeItr->next();
9266       if ( !volume ) continue;
9267
9268       const SMDSAbs_EntityType type = volume->GetEntityType();
9269       if ( volume->IsQuadratic() )
9270       {
9271         bool alreadyOK;
9272         switch ( type )
9273         {
9274         case SMDSEntity_Quad_Hexa:    alreadyOK = !theToBiQuad; break;
9275         case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9276         case SMDSEntity_Quad_Penta:   alreadyOK = !theToBiQuad; break;
9277         case SMDSEntity_BiQuad_Penta: alreadyOK = theToBiQuad; break;
9278         default:                      alreadyOK = true;
9279         }
9280         if ( alreadyOK )
9281         {
9282           aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
9283           continue;
9284         }
9285       }
9286       const int id = volume->GetID();
9287       vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9288       if ( type == SMDSEntity_Polyhedra )
9289         nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
9290       else if ( type == SMDSEntity_Hexagonal_Prism )
9291         volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9292
9293       meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9294
9295       SMDS_MeshVolume * NewVolume = 0;
9296       switch ( type )
9297       {
9298       case SMDSEntity_Tetra:
9299         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9300         break;
9301       case SMDSEntity_Hexa:
9302       case SMDSEntity_Quad_Hexa:
9303       case SMDSEntity_TriQuad_Hexa:
9304         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9305                                       nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9306         for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
9307           if ( nodes[i]->NbInverseElements() == 0 )
9308             GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9309         break;
9310       case SMDSEntity_Pyramid:
9311         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9312                                       nodes[3], nodes[4], id, theForce3d);
9313         break;
9314       case SMDSEntity_Penta:
9315       case SMDSEntity_Quad_Penta:
9316       case SMDSEntity_BiQuad_Penta:
9317         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9318                                       nodes[3], nodes[4], nodes[5], id, theForce3d);
9319         for ( size_t i = 15; i < nodes.size(); ++i ) // rm central nodes
9320           if ( nodes[i]->NbInverseElements() == 0 )
9321             GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9322         break;
9323       case SMDSEntity_Hexagonal_Prism:
9324       default:
9325         NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9326       }
9327       ReplaceElemInGroups(volume, NewVolume, meshDS);
9328     }
9329   }
9330
9331   if ( !theForce3d )
9332   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9333     // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9334     // aHelper.FixQuadraticElements(myError);
9335     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9336   }
9337 }
9338
9339 //================================================================================
9340 /*!
9341  * \brief Makes given elements quadratic
9342  *  \param theForce3d - if true, the medium nodes will be placed in the middle of link
9343  *  \param theElements - elements to make quadratic
9344  */
9345 //================================================================================
9346
9347 void SMESH_MeshEditor::ConvertToQuadratic(const bool        theForce3d,
9348                                           TIDSortedElemSet& theElements,
9349                                           const bool        theToBiQuad)
9350 {
9351   if ( theElements.empty() ) return;
9352
9353   // we believe that all theElements are of the same type
9354   const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9355
9356   // get all nodes shared by theElements
9357   TIDSortedNodeSet allNodes;
9358   TIDSortedElemSet::iterator eIt = theElements.begin();
9359   for ( ; eIt != theElements.end(); ++eIt )
9360     allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9361
9362   // complete theElements with elements of lower dim whose all nodes are in allNodes
9363
9364   TIDSortedElemSet quadAdjacentElems    [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9365   TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9366   TIDSortedNodeSet::iterator nIt = allNodes.begin();
9367   for ( ; nIt != allNodes.end(); ++nIt )
9368   {
9369     const SMDS_MeshNode* n = *nIt;
9370     SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9371     while ( invIt->more() )
9372     {
9373       const SMDS_MeshElement*      e = invIt->next();
9374       const SMDSAbs_ElementType type = e->GetType();
9375       if ( e->IsQuadratic() )
9376       {
9377         quadAdjacentElems[ type ].insert( e );
9378
9379         bool alreadyOK;
9380         switch ( e->GetEntityType() ) {
9381         case SMDSEntity_Quad_Triangle:
9382         case SMDSEntity_Quad_Quadrangle:
9383         case SMDSEntity_Quad_Hexa:         alreadyOK = !theToBiQuad; break;
9384         case SMDSEntity_BiQuad_Triangle:
9385         case SMDSEntity_BiQuad_Quadrangle:
9386         case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; break;
9387         default:                           alreadyOK = true;
9388         }
9389         if ( alreadyOK )
9390           continue;
9391       }
9392       if ( type >= elemType )
9393         continue; // same type or more complex linear element
9394
9395       if ( !checkedAdjacentElems[ type ].insert( e ).second )
9396         continue; // e is already checked
9397
9398       // check nodes
9399       bool allIn = true;
9400       SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9401       while ( nodeIt->more() && allIn )
9402         allIn = allNodes.count( nodeIt->next() );
9403       if ( allIn )
9404         theElements.insert(e );
9405     }
9406   }
9407
9408   SMESH_MesherHelper helper(*myMesh);
9409   helper.SetIsQuadratic( true );
9410   helper.SetIsBiQuadratic( theToBiQuad );
9411
9412   // add links of quadratic adjacent elements to the helper
9413
9414   if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9415     for ( eIt  = quadAdjacentElems[SMDSAbs_Edge].begin();
9416           eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9417     {
9418       helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9419     }
9420   if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9421     for ( eIt  = quadAdjacentElems[SMDSAbs_Face].begin();
9422           eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9423     {
9424       helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9425     }
9426   if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9427     for ( eIt  = quadAdjacentElems[SMDSAbs_Volume].begin();
9428           eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9429     {
9430       helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9431     }
9432
9433   // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9434
9435   SMESHDS_Mesh*  meshDS = GetMeshDS();
9436   SMESHDS_SubMesh* smDS = 0;
9437   for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9438   {
9439     const SMDS_MeshElement* elem = *eIt;
9440
9441     bool alreadyOK;
9442     int nbCentralNodes = 0;
9443     switch ( elem->GetEntityType() ) {
9444       // linear convertible
9445     case SMDSEntity_Edge:
9446     case SMDSEntity_Triangle:
9447     case SMDSEntity_Quadrangle:
9448     case SMDSEntity_Tetra:
9449     case SMDSEntity_Pyramid:
9450     case SMDSEntity_Hexa:
9451     case SMDSEntity_Penta:             alreadyOK = false;       nbCentralNodes = 0; break;
9452       // quadratic that can become bi-quadratic
9453     case SMDSEntity_Quad_Triangle:
9454     case SMDSEntity_Quad_Quadrangle:
9455     case SMDSEntity_Quad_Hexa:         alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9456       // bi-quadratic
9457     case SMDSEntity_BiQuad_Triangle:
9458     case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9459     case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9460       // the rest
9461     default:                           alreadyOK = true;
9462     }
9463     if ( alreadyOK ) continue;
9464
9465     const SMDSAbs_ElementType type = elem->GetType();
9466     const int                   id = elem->GetID();
9467     const int              nbNodes = elem->NbCornerNodes();
9468     vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9469
9470     helper.SetSubShape( elem->getshapeId() );
9471
9472     if ( !smDS || !smDS->Contains( elem ))
9473       smDS = meshDS->MeshElements( elem->getshapeId() );
9474     meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9475
9476     SMDS_MeshElement * newElem = 0;
9477     switch( nbNodes )
9478     {
9479     case 4: // cases for most frequently used element types go first (for optimization)
9480       if ( type == SMDSAbs_Volume )
9481         newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9482       else
9483         newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9484       break;
9485     case 8:
9486       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9487                                  nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9488       break;
9489     case 3:
9490       newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], id, theForce3d);
9491       break;
9492     case 2:
9493       newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9494       break;
9495     case 5:
9496       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9497                                  nodes[4], id, theForce3d);
9498       break;
9499     case 6:
9500       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9501                                  nodes[4], nodes[5], id, theForce3d);
9502       break;
9503     default:;
9504     }
9505     ReplaceElemInGroups( elem, newElem, meshDS);
9506     if( newElem && smDS )
9507       smDS->AddElement( newElem );
9508
9509     // remove central nodes
9510     for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9511       if ( nodes[i]->NbInverseElements() == 0 )
9512         meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9513
9514   } // loop on theElements
9515
9516   if ( !theForce3d )
9517   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9518     // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9519     // helper.FixQuadraticElements( myError );
9520     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9521   }
9522 }
9523
9524 //=======================================================================
9525 /*!
9526  * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9527  * \return int - nb of checked elements
9528  */
9529 //=======================================================================
9530
9531 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh *    theSm,
9532                                      SMDS_ElemIteratorPtr theItr,
9533                                      const int            theShapeID)
9534 {
9535   int nbElem = 0;
9536   SMESHDS_Mesh* meshDS = GetMeshDS();
9537   ElemFeatures elemType;
9538   vector<const SMDS_MeshNode *> nodes;
9539
9540   while( theItr->more() )
9541   {
9542     const SMDS_MeshElement* elem = theItr->next();
9543     nbElem++;
9544     if( elem && elem->IsQuadratic())
9545     {
9546       // get elem data
9547       int nbCornerNodes = elem->NbCornerNodes();
9548       nodes.assign( elem->begin_nodes(), elem->end_nodes() );
9549
9550       elemType.Init( elem, /*basicOnly=*/false ).SetID( elem->GetID() ).SetQuad( false );
9551
9552       //remove a quadratic element
9553       if ( !theSm || !theSm->Contains( elem ))
9554         theSm = meshDS->MeshElements( elem->getshapeId() );
9555       meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9556
9557       // remove medium nodes
9558       for ( size_t i = nbCornerNodes; i < nodes.size(); ++i )
9559         if ( nodes[i]->NbInverseElements() == 0 )
9560           meshDS->RemoveFreeNode( nodes[i], theSm );
9561
9562       // add a linear element
9563       nodes.resize( nbCornerNodes );
9564       SMDS_MeshElement * newElem = AddElement( nodes, elemType );
9565       ReplaceElemInGroups(elem, newElem, meshDS);
9566       if( theSm && newElem )
9567         theSm->AddElement( newElem );
9568     }
9569   }
9570   return nbElem;
9571 }
9572
9573 //=======================================================================
9574 //function : ConvertFromQuadratic
9575 //purpose  :
9576 //=======================================================================
9577
9578 bool SMESH_MeshEditor::ConvertFromQuadratic()
9579 {
9580   int nbCheckedElems = 0;
9581   if ( myMesh->HasShapeToMesh() )
9582   {
9583     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9584     {
9585       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9586       while ( smIt->more() ) {
9587         SMESH_subMesh* sm = smIt->next();
9588         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9589           nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9590       }
9591     }
9592   }
9593
9594   int totalNbElems =
9595     GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9596   if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9597   {
9598     SMESHDS_SubMesh *aSM = 0;
9599     removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9600   }
9601
9602   return true;
9603 }
9604
9605 namespace
9606 {
9607   //================================================================================
9608   /*!
9609    * \brief Return true if all medium nodes of the element are in the node set
9610    */
9611   //================================================================================
9612
9613   bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9614   {
9615     for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9616       if ( !nodeSet.count( elem->GetNode(i) ))
9617         return false;
9618     return true;
9619   }
9620 }
9621
9622 //================================================================================
9623 /*!
9624  * \brief Makes given elements linear
9625  */
9626 //================================================================================
9627
9628 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9629 {
9630   if ( theElements.empty() ) return;
9631
9632   // collect IDs of medium nodes of theElements; some of these nodes will be removed
9633   set<int> mediumNodeIDs;
9634   TIDSortedElemSet::iterator eIt = theElements.begin();
9635   for ( ; eIt != theElements.end(); ++eIt )
9636   {
9637     const SMDS_MeshElement* e = *eIt;
9638     for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9639       mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9640   }
9641
9642   // replace given elements by linear ones
9643   SMDS_ElemIteratorPtr elemIt = SMESHUtils::elemSetIterator( theElements );
9644   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9645
9646   // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9647   // except those elements sharing medium nodes of quadratic element whose medium nodes
9648   // are not all in mediumNodeIDs
9649
9650   // get remaining medium nodes
9651   TIDSortedNodeSet mediumNodes;
9652   set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9653   for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9654     if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9655       mediumNodes.insert( mediumNodes.end(), n );
9656
9657   // find more quadratic elements to convert
9658   TIDSortedElemSet moreElemsToConvert;
9659   TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9660   for ( ; nIt != mediumNodes.end(); ++nIt )
9661   {
9662     SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9663     while ( invIt->more() )
9664     {
9665       const SMDS_MeshElement* e = invIt->next();
9666       if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9667       {
9668         // find a more complex element including e and
9669         // whose medium nodes are not in mediumNodes
9670         bool complexFound = false;
9671         for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9672         {
9673           SMDS_ElemIteratorPtr invIt2 =
9674             (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9675           while ( invIt2->more() )
9676           {
9677             const SMDS_MeshElement* eComplex = invIt2->next();
9678             if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9679             {
9680               int nbCommonNodes = SMESH_MeshAlgos::GetCommonNodes( e, eComplex ).size();
9681               if ( nbCommonNodes == e->NbNodes())
9682               {
9683                 complexFound = true;
9684                 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9685                 break;
9686               }
9687             }
9688           }
9689         }
9690         if ( !complexFound )
9691           moreElemsToConvert.insert( e );
9692       }
9693     }
9694   }
9695   elemIt = SMESHUtils::elemSetIterator( moreElemsToConvert );
9696   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9697 }
9698
9699 //=======================================================================
9700 //function : SewSideElements
9701 //purpose  :
9702 //=======================================================================
9703
9704 SMESH_MeshEditor::Sew_Error
9705 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet&    theSide1,
9706                                    TIDSortedElemSet&    theSide2,
9707                                    const SMDS_MeshNode* theFirstNode1,
9708                                    const SMDS_MeshNode* theFirstNode2,
9709                                    const SMDS_MeshNode* theSecondNode1,
9710                                    const SMDS_MeshNode* theSecondNode2)
9711 {
9712   ClearLastCreated();
9713
9714   if ( theSide1.size() != theSide2.size() )
9715     return SEW_DIFF_NB_OF_ELEMENTS;
9716
9717   Sew_Error aResult = SEW_OK;
9718   // Algo:
9719   // 1. Build set of faces representing each side
9720   // 2. Find which nodes of the side 1 to merge with ones on the side 2
9721   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9722
9723   // =======================================================================
9724   // 1. Build set of faces representing each side:
9725   // =======================================================================
9726   // a. build set of nodes belonging to faces
9727   // b. complete set of faces: find missing faces whose nodes are in set of nodes
9728   // c. create temporary faces representing side of volumes if correspondent
9729   //    face does not exist
9730
9731   SMESHDS_Mesh* aMesh = GetMeshDS();
9732   // TODO algorithm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9733   //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9734   TIDSortedElemSet             faceSet1, faceSet2;
9735   set<const SMDS_MeshElement*> volSet1,  volSet2;
9736   set<const SMDS_MeshNode*>    nodeSet1, nodeSet2;
9737   TIDSortedElemSet             * faceSetPtr[] = { &faceSet1, &faceSet2 };
9738   set<const SMDS_MeshElement*> *  volSetPtr[] = { &volSet1,  &volSet2  };
9739   set<const SMDS_MeshNode*>    * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9740   TIDSortedElemSet             * elemSetPtr[] = { &theSide1, &theSide2 };
9741   int iSide, iFace, iNode;
9742
9743   list<const SMDS_MeshElement* > tempFaceList;
9744   for ( iSide = 0; iSide < 2; iSide++ ) {
9745     set<const SMDS_MeshNode*>    * nodeSet = nodeSetPtr[ iSide ];
9746     TIDSortedElemSet             * elemSet = elemSetPtr[ iSide ];
9747     TIDSortedElemSet             * faceSet = faceSetPtr[ iSide ];
9748     set<const SMDS_MeshElement*> * volSet  = volSetPtr [ iSide ];
9749     set<const SMDS_MeshElement*>::iterator vIt;
9750     TIDSortedElemSet::iterator eIt;
9751     set<const SMDS_MeshNode*>::iterator    nIt;
9752
9753     // check that given nodes belong to given elements
9754     const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9755     const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9756     int firstIndex = -1, secondIndex = -1;
9757     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9758       const SMDS_MeshElement* elem = *eIt;
9759       if ( firstIndex  < 0 ) firstIndex  = elem->GetNodeIndex( n1 );
9760       if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9761       if ( firstIndex > -1 && secondIndex > -1 ) break;
9762     }
9763     if ( firstIndex < 0 || secondIndex < 0 ) {
9764       // we can simply return until temporary faces created
9765       return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9766     }
9767
9768     // -----------------------------------------------------------
9769     // 1a. Collect nodes of existing faces
9770     //     and build set of face nodes in order to detect missing
9771     //     faces corresponding to sides of volumes
9772     // -----------------------------------------------------------
9773
9774     set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9775
9776     // loop on the given element of a side
9777     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9778       //const SMDS_MeshElement* elem = *eIt;
9779       const SMDS_MeshElement* elem = *eIt;
9780       if ( elem->GetType() == SMDSAbs_Face ) {
9781         faceSet->insert( elem );
9782         set <const SMDS_MeshNode*> faceNodeSet;
9783         SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9784         while ( nodeIt->more() ) {
9785           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9786           nodeSet->insert( n );
9787           faceNodeSet.insert( n );
9788         }
9789         setOfFaceNodeSet.insert( faceNodeSet );
9790       }
9791       else if ( elem->GetType() == SMDSAbs_Volume )
9792         volSet->insert( elem );
9793     }
9794     // ------------------------------------------------------------------------------
9795     // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9796     // ------------------------------------------------------------------------------
9797
9798     for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9799       SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9800       while ( fIt->more() ) { // loop on faces sharing a node
9801         const SMDS_MeshElement* f = fIt->next();
9802         if ( faceSet->find( f ) == faceSet->end() ) {
9803           // check if all nodes are in nodeSet and
9804           // complete setOfFaceNodeSet if they are
9805           set <const SMDS_MeshNode*> faceNodeSet;
9806           SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9807           bool allInSet = true;
9808           while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9809             const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9810             if ( nodeSet->find( n ) == nodeSet->end() )
9811               allInSet = false;
9812             else
9813               faceNodeSet.insert( n );
9814           }
9815           if ( allInSet ) {
9816             faceSet->insert( f );
9817             setOfFaceNodeSet.insert( faceNodeSet );
9818           }
9819         }
9820       }
9821     }
9822
9823     // -------------------------------------------------------------------------
9824     // 1c. Create temporary faces representing sides of volumes if correspondent
9825     //     face does not exist
9826     // -------------------------------------------------------------------------
9827
9828     if ( !volSet->empty() ) {
9829       //int nodeSetSize = nodeSet->size();
9830
9831       // loop on given volumes
9832       for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
9833         SMDS_VolumeTool vol (*vIt);
9834         // loop on volume faces: find free faces
9835         // --------------------------------------
9836         list<const SMDS_MeshElement* > freeFaceList;
9837         for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
9838           if ( !vol.IsFreeFace( iFace ))
9839             continue;
9840           // check if there is already a face with same nodes in a face set
9841           const SMDS_MeshElement* aFreeFace = 0;
9842           const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
9843           int nbNodes = vol.NbFaceNodes( iFace );
9844           set <const SMDS_MeshNode*> faceNodeSet;
9845           vol.GetFaceNodes( iFace, faceNodeSet );
9846           bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
9847           if ( isNewFace ) {
9848             // no such a face is given but it still can exist, check it
9849             vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
9850             aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
9851           }
9852           if ( !aFreeFace ) {
9853             // create a temporary face
9854             if ( nbNodes == 3 ) {
9855               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
9856               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
9857             }
9858             else if ( nbNodes == 4 ) {
9859               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9860               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9861             }
9862             else {
9863               vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9864               //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
9865               aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
9866             }
9867             if ( aFreeFace )
9868               tempFaceList.push_back( aFreeFace );
9869           }
9870
9871           if ( aFreeFace )
9872             freeFaceList.push_back( aFreeFace );
9873
9874         } // loop on faces of a volume
9875
9876         // choose one of several free faces of a volume
9877         // --------------------------------------------
9878         if ( freeFaceList.size() > 1 ) {
9879           // choose a face having max nb of nodes shared by other elems of a side
9880           int maxNbNodes = -1;
9881           list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
9882           while ( fIt != freeFaceList.end() ) { // loop on free faces
9883             int nbSharedNodes = 0;
9884             SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9885             while ( nodeIt->more() ) { // loop on free face nodes
9886               const SMDS_MeshNode* n =
9887                 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9888               SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
9889               while ( invElemIt->more() ) {
9890                 const SMDS_MeshElement* e = invElemIt->next();
9891                 nbSharedNodes += faceSet->count( e );
9892                 nbSharedNodes += elemSet->count( e );
9893               }
9894             }
9895             if ( nbSharedNodes > maxNbNodes ) {
9896               maxNbNodes = nbSharedNodes;
9897               freeFaceList.erase( freeFaceList.begin(), fIt++ );
9898             }
9899             else if ( nbSharedNodes == maxNbNodes ) {
9900               fIt++;
9901             }
9902             else {
9903               freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
9904             }
9905           }
9906           if ( freeFaceList.size() > 1 )
9907           {
9908             // could not choose one face, use another way
9909             // choose a face most close to the bary center of the opposite side
9910             gp_XYZ aBC( 0., 0., 0. );
9911             set <const SMDS_MeshNode*> addedNodes;
9912             TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
9913             eIt = elemSet2->begin();
9914             for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
9915               SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
9916               while ( nodeIt->more() ) { // loop on free face nodes
9917                 const SMDS_MeshNode* n =
9918                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9919                 if ( addedNodes.insert( n ).second )
9920                   aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
9921               }
9922             }
9923             aBC /= addedNodes.size();
9924             double minDist = DBL_MAX;
9925             fIt = freeFaceList.begin();
9926             while ( fIt != freeFaceList.end() ) { // loop on free faces
9927               double dist = 0;
9928               SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9929               while ( nodeIt->more() ) { // loop on free face nodes
9930                 const SMDS_MeshNode* n =
9931                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9932                 gp_XYZ p( n->X(),n->Y(),n->Z() );
9933                 dist += ( aBC - p ).SquareModulus();
9934               }
9935               if ( dist < minDist ) {
9936                 minDist = dist;
9937                 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9938               }
9939               else
9940                 fIt = freeFaceList.erase( fIt++ );
9941             }
9942           }
9943         } // choose one of several free faces of a volume
9944
9945         if ( freeFaceList.size() == 1 ) {
9946           const SMDS_MeshElement* aFreeFace = freeFaceList.front();
9947           faceSet->insert( aFreeFace );
9948           // complete a node set with nodes of a found free face
9949           //           for ( iNode = 0; iNode < ; iNode++ )
9950           //             nodeSet->insert( fNodes[ iNode ] );
9951         }
9952
9953       } // loop on volumes of a side
9954
9955       //       // complete a set of faces if new nodes in a nodeSet appeared
9956       //       // ----------------------------------------------------------
9957       //       if ( nodeSetSize != nodeSet->size() ) {
9958       //         for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9959       //           SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9960       //           while ( fIt->more() ) { // loop on faces sharing a node
9961       //             const SMDS_MeshElement* f = fIt->next();
9962       //             if ( faceSet->find( f ) == faceSet->end() ) {
9963       //               // check if all nodes are in nodeSet and
9964       //               // complete setOfFaceNodeSet if they are
9965       //               set <const SMDS_MeshNode*> faceNodeSet;
9966       //               SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9967       //               bool allInSet = true;
9968       //               while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9969       //                 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9970       //                 if ( nodeSet->find( n ) == nodeSet->end() )
9971       //                   allInSet = false;
9972       //                 else
9973       //                   faceNodeSet.insert( n );
9974       //               }
9975       //               if ( allInSet ) {
9976       //                 faceSet->insert( f );
9977       //                 setOfFaceNodeSet.insert( faceNodeSet );
9978       //               }
9979       //             }
9980       //           }
9981       //         }
9982       //       }
9983     } // Create temporary faces, if there are volumes given
9984   } // loop on sides
9985
9986   if ( faceSet1.size() != faceSet2.size() ) {
9987     // delete temporary faces: they are in reverseElements of actual nodes
9988     //    SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9989     //    while ( tmpFaceIt->more() )
9990     //      aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9991     //    list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9992     //    for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9993     //      aMesh->RemoveElement(*tmpFaceIt);
9994     MESSAGE("Diff nb of faces");
9995     return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9996   }
9997
9998   // ============================================================
9999   // 2. Find nodes to merge:
10000   //              bind a node to remove to a node to put instead
10001   // ============================================================
10002
10003   TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
10004   if ( theFirstNode1 != theFirstNode2 )
10005     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10006   if ( theSecondNode1 != theSecondNode2 )
10007     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10008
10009   LinkID_Gen aLinkID_Gen( GetMeshDS() );
10010   set< long > linkIdSet; // links to process
10011   linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
10012
10013   typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
10014   list< NLink > linkList[2];
10015   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10016   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10017   // loop on links in linkList; find faces by links and append links
10018   // of the found faces to linkList
10019   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10020   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
10021   {
10022     NLink link[] = { *linkIt[0], *linkIt[1] };
10023     long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
10024     if ( !linkIdSet.count( linkID ) )
10025       continue;
10026
10027     // by links, find faces in the face sets,
10028     // and find indices of link nodes in the found faces;
10029     // in a face set, there is only one or no face sharing a link
10030     // ---------------------------------------------------------------
10031
10032     const SMDS_MeshElement* face[] = { 0, 0 };
10033     vector<const SMDS_MeshNode*> fnodes[2];
10034     int iLinkNode[2][2];
10035     TIDSortedElemSet avoidSet;
10036     for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10037       const SMDS_MeshNode* n1 = link[iSide].first;
10038       const SMDS_MeshNode* n2 = link[iSide].second;
10039       //cout << "Side " << iSide << " ";
10040       //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
10041       // find a face by two link nodes
10042       face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
10043                                                       *faceSetPtr[ iSide ], avoidSet,
10044                                                       &iLinkNode[iSide][0],
10045                                                       &iLinkNode[iSide][1] );
10046       if ( face[ iSide ])
10047       {
10048         //cout << " F " << face[ iSide]->GetID() <<endl;
10049         faceSetPtr[ iSide ]->erase( face[ iSide ]);
10050         // put face nodes to fnodes
10051         if ( face[ iSide ]->IsQuadratic() )
10052         {
10053           // use interlaced nodes iterator
10054           const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>( face[ iSide ]);
10055           if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
10056           SMDS_ElemIteratorPtr nIter = F->interlacedNodesElemIterator();
10057           while ( nIter->more() )
10058             fnodes[ iSide ].push_back( cast2Node( nIter->next() ));
10059         }
10060         else
10061         {
10062           fnodes[ iSide ].assign( face[ iSide ]->begin_nodes(),
10063                                   face[ iSide ]->end_nodes() );
10064         }
10065         fnodes[ iSide ].push_back( fnodes[ iSide ].front());
10066       }
10067     }
10068
10069     // check similarity of elements of the sides
10070     if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10071       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10072       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10073         aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10074       }
10075       else {
10076         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10077       }
10078       break; // do not return because it's necessary to remove tmp faces
10079     }
10080
10081     // set nodes to merge
10082     // -------------------
10083
10084     if ( face[0] && face[1] )  {
10085       const int nbNodes = face[0]->NbNodes();
10086       if ( nbNodes != face[1]->NbNodes() ) {
10087         MESSAGE("Diff nb of face nodes");
10088         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10089         break; // do not return because it s necessary to remove tmp faces
10090       }
10091       bool reverse[] = { false, false }; // order of nodes in the link
10092       for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10093         // analyse link orientation in faces
10094         int i1 = iLinkNode[ iSide ][ 0 ];
10095         int i2 = iLinkNode[ iSide ][ 1 ];
10096         reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10097       }
10098       int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
10099       int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
10100       for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
10101       {
10102         nReplaceMap.insert  ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
10103                                           fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
10104       }
10105
10106       // add other links of the faces to linkList
10107       // -----------------------------------------
10108
10109       for ( iNode = 0; iNode < nbNodes; iNode++ )  {
10110         linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
10111         pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10112         if ( !iter_isnew.second ) { // already in a set: no need to process
10113           linkIdSet.erase( iter_isnew.first );
10114         }
10115         else // new in set == encountered for the first time: add
10116         {
10117           const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
10118           const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
10119           linkList[0].push_back ( NLink( n1, n2 ));
10120           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10121         }
10122       }
10123     } // 2 faces found
10124
10125     if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
10126       break;
10127
10128   } // loop on link lists
10129
10130   if ( aResult == SEW_OK &&
10131        ( //linkIt[0] != linkList[0].end() ||
10132         !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10133     MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10134              " " << (faceSetPtr[1]->empty()));
10135     aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10136   }
10137
10138   // ====================================================================
10139   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10140   // ====================================================================
10141
10142   // delete temporary faces
10143   //  SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10144   //  while ( tmpFaceIt->more() )
10145   //    aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10146   list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10147   for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10148     aMesh->RemoveElement(*tmpFaceIt);
10149
10150   if ( aResult != SEW_OK)
10151     return aResult;
10152
10153   list< int > nodeIDsToRemove;
10154   vector< const SMDS_MeshNode*> nodes;
10155   ElemFeatures elemType;
10156
10157   // loop on nodes replacement map
10158   TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10159   for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10160     if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second )
10161     {
10162       const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10163       nodeIDsToRemove.push_back( nToRemove->GetID() );
10164       // loop on elements sharing nToRemove
10165       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10166       while ( invElemIt->more() ) {
10167         const SMDS_MeshElement* e = invElemIt->next();
10168         // get a new suite of nodes: make replacement
10169         int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10170         nodes.resize( nbNodes );
10171         SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10172         while ( nIt->more() ) {
10173           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
10174           nnIt = nReplaceMap.find( n );
10175           if ( nnIt != nReplaceMap.end() ) {
10176             nbReplaced++;
10177             n = (*nnIt).second;
10178           }
10179           nodes[ i++ ] = n;
10180         }
10181         //       if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10182         //         elemIDsToRemove.push_back( e->GetID() );
10183         //       else
10184         if ( nbReplaced )
10185         {
10186           elemType.Init( e, /*basicOnly=*/false ).SetID( e->GetID() );
10187           aMesh->RemoveElement( e );
10188
10189           if ( SMDS_MeshElement* newElem = this->AddElement( nodes, elemType ))
10190           {
10191             AddToSameGroups( newElem, e, aMesh );
10192             if ( int aShapeId = e->getshapeId() )
10193               aMesh->SetMeshElementOnShape( newElem, aShapeId );
10194           }
10195         }
10196       }
10197     }
10198
10199   Remove( nodeIDsToRemove, true );
10200
10201   return aResult;
10202 }
10203
10204 //================================================================================
10205 /*!
10206  * \brief Find corresponding nodes in two sets of faces
10207  * \param theSide1 - first face set
10208  * \param theSide2 - second first face
10209  * \param theFirstNode1 - a boundary node of set 1
10210  * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10211  * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10212  * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10213  * \param nReplaceMap - output map of corresponding nodes
10214  * \return bool  - is a success or not
10215  */
10216 //================================================================================
10217
10218 #ifdef _DEBUG_
10219 //#define DEBUG_MATCHING_NODES
10220 #endif
10221
10222 SMESH_MeshEditor::Sew_Error
10223 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10224                                     set<const SMDS_MeshElement*>& theSide2,
10225                                     const SMDS_MeshNode*          theFirstNode1,
10226                                     const SMDS_MeshNode*          theFirstNode2,
10227                                     const SMDS_MeshNode*          theSecondNode1,
10228                                     const SMDS_MeshNode*          theSecondNode2,
10229                                     TNodeNodeMap &                nReplaceMap)
10230 {
10231   set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10232
10233   nReplaceMap.clear();
10234   if ( theFirstNode1 != theFirstNode2 )
10235     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10236   if ( theSecondNode1 != theSecondNode2 )
10237     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10238
10239   set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10240   linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10241
10242   list< NLink > linkList[2];
10243   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10244   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10245
10246   // loop on links in linkList; find faces by links and append links
10247   // of the found faces to linkList
10248   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10249   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10250     NLink link[] = { *linkIt[0], *linkIt[1] };
10251     if ( linkSet.find( link[0] ) == linkSet.end() )
10252       continue;
10253
10254     // by links, find faces in the face sets,
10255     // and find indices of link nodes in the found faces;
10256     // in a face set, there is only one or no face sharing a link
10257     // ---------------------------------------------------------------
10258
10259     const SMDS_MeshElement* face[] = { 0, 0 };
10260     list<const SMDS_MeshNode*> notLinkNodes[2];
10261     //bool reverse[] = { false, false }; // order of notLinkNodes
10262     int nbNodes[2];
10263     for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10264     {
10265       const SMDS_MeshNode* n1 = link[iSide].first;
10266       const SMDS_MeshNode* n2 = link[iSide].second;
10267       set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10268       set< const SMDS_MeshElement* > facesOfNode1;
10269       for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10270       {
10271         // during a loop of the first node, we find all faces around n1,
10272         // during a loop of the second node, we find one face sharing both n1 and n2
10273         const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10274         SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10275         while ( fIt->more() ) { // loop on faces sharing a node
10276           const SMDS_MeshElement* f = fIt->next();
10277           if (faceSet->find( f ) != faceSet->end() && // f is in face set
10278               ! facesOfNode1.insert( f ).second ) // f encounters twice
10279           {
10280             if ( face[ iSide ] ) {
10281               MESSAGE( "2 faces per link " );
10282               return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10283             }
10284             face[ iSide ] = f;
10285             faceSet->erase( f );
10286
10287             // get not link nodes
10288             int nbN = f->NbNodes();
10289             if ( f->IsQuadratic() )
10290               nbN /= 2;
10291             nbNodes[ iSide ] = nbN;
10292             list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10293             int i1 = f->GetNodeIndex( n1 );
10294             int i2 = f->GetNodeIndex( n2 );
10295             int iEnd = nbN, iBeg = -1, iDelta = 1;
10296             bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10297             if ( reverse ) {
10298               std::swap( iEnd, iBeg ); iDelta = -1;
10299             }
10300             int i = i2;
10301             while ( true ) {
10302               i += iDelta;
10303               if ( i == iEnd ) i = iBeg + iDelta;
10304               if ( i == i1 ) break;
10305               nodes.push_back ( f->GetNode( i ) );
10306             }
10307           }
10308         }
10309       }
10310     }
10311     // check similarity of elements of the sides
10312     if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10313       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10314       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10315         return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10316       }
10317       else {
10318         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10319       }
10320     }
10321
10322     // set nodes to merge
10323     // -------------------
10324
10325     if ( face[0] && face[1] )  {
10326       if ( nbNodes[0] != nbNodes[1] ) {
10327         MESSAGE("Diff nb of face nodes");
10328         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10329       }
10330 #ifdef DEBUG_MATCHING_NODES
10331       MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10332                 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10333                 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10334 #endif
10335       int nbN = nbNodes[0];
10336       {
10337         list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10338         list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10339         for ( int i = 0 ; i < nbN - 2; ++i ) {
10340 #ifdef DEBUG_MATCHING_NODES
10341           MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10342 #endif
10343           nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10344         }
10345       }
10346
10347       // add other links of the face 1 to linkList
10348       // -----------------------------------------
10349
10350       const SMDS_MeshElement* f0 = face[0];
10351       const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10352       for ( int i = 0; i < nbN; i++ )
10353       {
10354         const SMDS_MeshNode* n2 = f0->GetNode( i );
10355         pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10356           linkSet.insert( SMESH_TLink( n1, n2 ));
10357         if ( !iter_isnew.second ) { // already in a set: no need to process
10358           linkSet.erase( iter_isnew.first );
10359         }
10360         else // new in set == encountered for the first time: add
10361         {
10362 #ifdef DEBUG_MATCHING_NODES
10363           MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10364                     << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10365 #endif
10366           linkList[0].push_back ( NLink( n1, n2 ));
10367           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10368         }
10369         n1 = n2;
10370       }
10371     } // 2 faces found
10372   } // loop on link lists
10373
10374   return SEW_OK;
10375 }
10376
10377 namespace // automatically find theAffectedElems for DoubleNodes()
10378 {
10379   bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem );
10380
10381   //--------------------------------------------------------------------------------
10382   // Nodes shared by adjacent FissureBorder's.
10383   // 1 node  if FissureBorder separates faces
10384   // 2 nodes if FissureBorder separates volumes
10385   struct SubBorder
10386   {
10387     const SMDS_MeshNode* _nodes[2];
10388     int                  _nbNodes;
10389
10390     SubBorder( const SMDS_MeshNode* n1, const SMDS_MeshNode* n2 = 0 )
10391     {
10392       _nodes[0] = n1;
10393       _nodes[1] = n2;
10394       _nbNodes = bool( n1 ) + bool( n2 );
10395       if ( _nbNodes == 2 && n1 > n2 )
10396         std::swap( _nodes[0], _nodes[1] );
10397     }
10398     bool operator<( const SubBorder& other ) const
10399     {
10400       for ( int i = 0; i < _nbNodes; ++i )
10401       {
10402         if ( _nodes[i] < other._nodes[i] ) return true;
10403         if ( _nodes[i] > other._nodes[i] ) return false;
10404       }
10405       return false;
10406     }
10407   };
10408
10409   //--------------------------------------------------------------------------------
10410   // Map a SubBorder to all FissureBorder it bounds
10411   struct FissureBorder;
10412   typedef std::map< SubBorder, std::vector< FissureBorder* > > TBorderLinks;
10413   typedef TBorderLinks::iterator                               TMappedSub;
10414
10415   //--------------------------------------------------------------------------------
10416   /*!
10417    * \brief Element border (volume facet or face edge) at a fissure
10418    */
10419   struct FissureBorder
10420   {
10421     std::vector< const SMDS_MeshNode* > _nodes;    // border nodes
10422     const SMDS_MeshElement*             _elems[2]; // volume or face adjacent to fissure
10423
10424     std::vector< TMappedSub           > _mappedSubs;  // Sub() in TBorderLinks map
10425     std::vector< const SMDS_MeshNode* > _sortedNodes; // to compare FissureBorder's
10426
10427     FissureBorder( FissureBorder && from ) // move constructor
10428     {
10429       std::swap( _nodes,       from._nodes );
10430       std::swap( _sortedNodes, from._sortedNodes );
10431       _elems[0] = from._elems[0];
10432       _elems[1] = from._elems[1];
10433     }
10434
10435     FissureBorder( const SMDS_MeshElement*                  elemToDuplicate,
10436                    std::vector< const SMDS_MeshElement* > & adjElems)
10437       : _nodes( elemToDuplicate->NbCornerNodes() )
10438     {
10439       for ( size_t i = 0; i < _nodes.size(); ++i )
10440         _nodes[i] = elemToDuplicate->GetNode( i );
10441
10442       SMDSAbs_ElementType type = SMDSAbs_ElementType( elemToDuplicate->GetType() + 1 );
10443       findAdjacent( type, adjElems );
10444     }
10445
10446     FissureBorder( const SMDS_MeshNode**                    nodes,
10447                    const size_t                             nbNodes,
10448                    const SMDSAbs_ElementType                adjElemsType,
10449                    std::vector< const SMDS_MeshElement* > & adjElems)
10450       : _nodes( nodes, nodes + nbNodes )
10451     {
10452       findAdjacent( adjElemsType, adjElems );
10453     }
10454
10455     void findAdjacent( const SMDSAbs_ElementType                adjElemsType,
10456                        std::vector< const SMDS_MeshElement* > & adjElems)
10457     {
10458       _elems[0] = _elems[1] = 0;
10459       adjElems.clear();
10460       if ( SMDS_Mesh::GetElementsByNodes( _nodes, adjElems, adjElemsType ))
10461         for ( size_t i = 0; i < adjElems.size() && i < 2; ++i )
10462           _elems[i] = adjElems[i];
10463     }
10464
10465     bool operator<( const FissureBorder& other ) const
10466     {
10467       return GetSortedNodes() < other.GetSortedNodes();
10468     }
10469
10470     const std::vector< const SMDS_MeshNode* >& GetSortedNodes() const
10471     {
10472       if ( _sortedNodes.empty() && !_nodes.empty() )
10473       {
10474         FissureBorder* me = const_cast<FissureBorder*>( this );
10475         me->_sortedNodes = me->_nodes;
10476         std::sort( me->_sortedNodes.begin(), me->_sortedNodes.end() );
10477       }
10478       return _sortedNodes;
10479     }
10480
10481     size_t NbSub() const
10482     {
10483       return _nodes.size();
10484     }
10485
10486     SubBorder Sub(size_t i) const
10487     {
10488       return SubBorder( _nodes[i], NbSub() > 2 ? _nodes[ (i+1)%NbSub() ] : 0 );
10489     }
10490
10491     void AddSelfTo( TBorderLinks& borderLinks )
10492     {
10493       _mappedSubs.resize( NbSub() );
10494       for ( size_t i = 0; i < NbSub(); ++i )
10495       {
10496         TBorderLinks::iterator s2b =
10497           borderLinks.insert( std::make_pair( Sub(i), TBorderLinks::mapped_type() )).first;
10498         s2b->second.push_back( this );
10499         _mappedSubs[ i ] = s2b;
10500       }
10501     }
10502
10503     void Clear()
10504     {
10505       _nodes.clear();
10506     }
10507
10508     const SMDS_MeshElement* GetMarkedElem() const
10509     {
10510       if ( _nodes.empty() ) return 0; // cleared
10511       if ( _elems[0] && _elems[0]->isMarked() ) return _elems[0];
10512       if ( _elems[1] && _elems[1]->isMarked() ) return _elems[1];
10513       return 0;
10514     }
10515
10516     gp_XYZ GetNorm() const // normal to the border
10517     {
10518       gp_XYZ norm;
10519       if ( _nodes.size() == 2 )
10520       {
10521         gp_XYZ avgNorm( 0,0,0 ); // sum of normals of adjacent faces
10522         if ( SMESH_MeshAlgos::FaceNormal( _elems[0], norm ))
10523           avgNorm += norm;
10524         if ( SMESH_MeshAlgos::FaceNormal( _elems[1], norm ))
10525           avgNorm += norm;
10526
10527         gp_XYZ bordDir( SMESH_NodeXYZ( _nodes[0] ) - SMESH_NodeXYZ( _nodes[1] ));
10528         norm = bordDir ^ avgNorm;
10529       }
10530       else
10531       {
10532         SMESH_NodeXYZ p0( _nodes[0] );
10533         SMESH_NodeXYZ p1( _nodes[1] );
10534         SMESH_NodeXYZ p2( _nodes[2] );
10535         norm = ( p0 - p1 ) ^ ( p2 - p1 );
10536       }
10537       if ( isOut( _nodes[0], norm, GetMarkedElem() ))
10538         norm.Reverse();
10539
10540       return norm;
10541     }
10542
10543     void ChooseSide() // mark an _elem located at positive side of fissure
10544     {
10545       _elems[0]->setIsMarked( true );
10546       gp_XYZ norm = GetNorm();
10547       double maxX = norm.Coord(1);
10548       if ( Abs( maxX ) < Abs( norm.Coord(2)) ) maxX = norm.Coord(2);
10549       if ( Abs( maxX ) < Abs( norm.Coord(3)) ) maxX = norm.Coord(3);
10550       if ( maxX < 0 )
10551       {
10552         _elems[0]->setIsMarked( false );
10553         _elems[1]->setIsMarked( true );
10554       }
10555     }
10556
10557   }; // struct FissureBorder
10558
10559   //--------------------------------------------------------------------------------
10560   /*!
10561    * \brief Classifier of elements at fissure edge
10562    */
10563   class FissureNormal
10564   {
10565     std::vector< gp_XYZ > _normals;
10566     bool                  _bothIn;
10567
10568   public:
10569     void Add( const SMDS_MeshNode* n, const FissureBorder& bord )
10570     {
10571       _bothIn = false;
10572       _normals.reserve(2);
10573       _normals.push_back( bord.GetNorm() );
10574       if ( _normals.size() == 2 )
10575         _bothIn = !isOut( n, _normals[0], bord.GetMarkedElem() );
10576     }
10577
10578     bool IsIn( const SMDS_MeshNode* n, const SMDS_MeshElement* elem ) const
10579     {
10580       bool isIn = false;
10581       switch ( _normals.size() ) {
10582       case 1:
10583       {
10584         isIn = !isOut( n, _normals[0], elem );
10585         break;
10586       }
10587       case 2:
10588       {
10589         bool in1 = !isOut( n, _normals[0], elem );
10590         bool in2 = !isOut( n, _normals[1], elem );
10591         isIn = _bothIn ? ( in1 && in2 ) : ( in1 || in2 );
10592       }
10593       }
10594       return isIn;
10595     }
10596   };
10597
10598   //================================================================================
10599   /*!
10600    * \brief Classify an element by a plane passing through a node
10601    */
10602   //================================================================================
10603
10604   bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem )
10605   {
10606     SMESH_NodeXYZ p = n;
10607     double sumDot = 0;
10608     for ( int i = 0, nb = elem->NbCornerNodes(); i < nb; ++i )
10609     {
10610       SMESH_NodeXYZ pi = elem->GetNode( i );
10611       sumDot += norm * ( pi - p );
10612     }
10613     return sumDot < -1e-100;
10614   }
10615
10616   //================================================================================
10617   /*!
10618    * \brief Find FissureBorder's by nodes to duplicate
10619    */
10620   //================================================================================
10621
10622   void findFissureBorders( const TIDSortedElemSet&        theNodes,
10623                            std::vector< FissureBorder > & theFissureBorders )
10624   {
10625     TIDSortedElemSet::const_iterator nIt = theNodes.begin();
10626     const SMDS_MeshNode* n = dynamic_cast< const SMDS_MeshNode*>( *nIt );
10627     if ( !n ) return;
10628     SMDSAbs_ElementType elemType = SMDSAbs_Volume;
10629     if ( n->NbInverseElements( elemType ) == 0 )
10630     {
10631       elemType = SMDSAbs_Face;
10632       if ( n->NbInverseElements( elemType ) == 0 )
10633         return;
10634     }
10635     // unmark elements touching the fissure
10636     for ( ; nIt != theNodes.end(); ++nIt )
10637       SMESH_MeshAlgos::MarkElems( cast2Node(*nIt)->GetInverseElementIterator(), false );
10638
10639     // loop on elements touching the fissure to get their borders belonging to the fissure
10640     std::set< FissureBorder >              fissureBorders;
10641     std::vector< const SMDS_MeshElement* > adjElems;
10642     std::vector< const SMDS_MeshNode* >    nodes;
10643     SMDS_VolumeTool volTool;
10644     for ( nIt = theNodes.begin(); nIt != theNodes.end(); ++nIt )
10645     {
10646       SMDS_ElemIteratorPtr invIt = cast2Node(*nIt)->GetInverseElementIterator( elemType );
10647       while ( invIt->more() )
10648       {
10649         const SMDS_MeshElement* eInv = invIt->next();
10650         if ( eInv->isMarked() ) continue;
10651         eInv->setIsMarked( true );
10652
10653         if ( elemType == SMDSAbs_Volume )
10654         {
10655           volTool.Set( eInv );
10656           int iQuad = eInv->IsQuadratic() ? 2 : 1;
10657           for ( int iF = 0, nbF = volTool.NbFaces(); iF < nbF; ++iF )
10658           {
10659             const SMDS_MeshNode** nn = volTool.GetFaceNodes( iF );
10660             int                  nbN = volTool.NbFaceNodes( iF ) / iQuad;
10661             nodes.clear();
10662             bool allOnFissure = true;
10663             for ( int iN = 0; iN < nbN  && allOnFissure; iN += iQuad )
10664               if (( allOnFissure = theNodes.count( nn[ iN ])))
10665                 nodes.push_back( nn[ iN ]);
10666             if ( allOnFissure )
10667               fissureBorders.insert( std::move( FissureBorder( &nodes[0], nodes.size(),
10668                                                                elemType, adjElems )));
10669           }
10670         }
10671         else // elemType == SMDSAbs_Face
10672         {
10673           const SMDS_MeshNode* nn[2] = { eInv->GetNode( eInv->NbCornerNodes()-1 ), 0 };
10674           bool            onFissure0 = theNodes.count( nn[0] ), onFissure1;
10675           for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN; ++iN )
10676           {
10677             nn[1]      = eInv->GetNode( iN );
10678             onFissure1 = theNodes.count( nn[1] );
10679             if ( onFissure0 && onFissure1 )
10680               fissureBorders.insert( std::move( FissureBorder( nn, 2, elemType, adjElems )));
10681             nn[0]      = nn[1];
10682             onFissure0 = onFissure1;
10683           }
10684         }
10685       }
10686     }
10687
10688     theFissureBorders.reserve( theFissureBorders.size() + fissureBorders.size());
10689     std::set< FissureBorder >::iterator bord = fissureBorders.begin();
10690     for ( ; bord != fissureBorders.end(); ++bord )
10691     {
10692       theFissureBorders.push_back( std::move( const_cast<FissureBorder&>( *bord ) ));
10693     }
10694     return;
10695   } // findFissureBorders()
10696
10697   //================================================================================
10698   /*!
10699    * \brief Find elements on one side of a fissure defined by elements or nodes to duplicate
10700    *  \param [in] theElemsOrNodes - elements or nodes to duplicate
10701    *  \param [in] theNodesNot - nodes not to duplicate
10702    *  \param [out] theAffectedElems - the found elements
10703    */
10704   //================================================================================
10705
10706   void findAffectedElems( const TIDSortedElemSet& theElemsOrNodes,
10707                           TIDSortedElemSet&       theAffectedElems)
10708   {
10709     if ( theElemsOrNodes.empty() ) return;
10710
10711     // find FissureBorder's
10712
10713     std::vector< FissureBorder >           fissure;
10714     std::vector< const SMDS_MeshElement* > elemsByFacet;
10715
10716     TIDSortedElemSet::const_iterator elIt = theElemsOrNodes.begin();
10717     if ( (*elIt)->GetType() == SMDSAbs_Node )
10718     {
10719       findFissureBorders( theElemsOrNodes, fissure );
10720     }
10721     else
10722     {
10723       fissure.reserve( theElemsOrNodes.size() );
10724       for ( ; elIt != theElemsOrNodes.end(); ++elIt )
10725         fissure.push_back( std::move( FissureBorder( *elIt, elemsByFacet )));
10726     }
10727     if ( fissure.empty() )
10728       return;
10729
10730     // fill borderLinks
10731
10732     TBorderLinks borderLinks;
10733
10734     for ( size_t i = 0; i < fissure.size(); ++i )
10735     {
10736       fissure[i].AddSelfTo( borderLinks );
10737     }
10738
10739     // get theAffectedElems
10740
10741     // unmark elements having nodes on the fissure, theAffectedElems elements will be marked
10742     for ( size_t i = 0; i < fissure.size(); ++i )
10743       for ( size_t j = 0; j < fissure[i]._nodes.size(); ++j )
10744       {
10745         SMESH_MeshAlgos::MarkElemNodes( fissure[i]._nodes[j]->GetInverseElementIterator(),
10746                                         false, /*markElem=*/true );
10747       }
10748
10749     std::vector<const SMDS_MeshNode *>                 facetNodes;
10750     std::map< const SMDS_MeshNode*, FissureNormal >    fissEdgeNodes2Norm;
10751     boost::container::flat_set< const SMDS_MeshNode* > fissureNodes;
10752
10753     // choose a side of fissure
10754     fissure[0].ChooseSide();
10755     theAffectedElems.insert( fissure[0].GetMarkedElem() );
10756
10757     size_t nbCheckedBorders = 0;
10758     while ( nbCheckedBorders < fissure.size() )
10759     {
10760       // find a FissureBorder to treat
10761       FissureBorder* bord = 0;
10762       for ( size_t i = 0; i < fissure.size()  && !bord; ++i )
10763         if ( fissure[i].GetMarkedElem() )
10764           bord = & fissure[i];
10765       for ( size_t i = 0; i < fissure.size()  && !bord; ++i )
10766         if ( fissure[i].NbSub() > 0 && fissure[i]._elems[0] )
10767         {
10768           bord = & fissure[i];
10769           bord->ChooseSide();
10770           theAffectedElems.insert( bord->GetMarkedElem() );
10771         }
10772       if ( !bord ) return;
10773       ++nbCheckedBorders;
10774
10775       // treat FissureBorder's linked to bord
10776       fissureNodes.clear();
10777       fissureNodes.insert( bord->_nodes.begin(), bord->_nodes.end() );
10778       for ( size_t i = 0; i < bord->NbSub(); ++i )
10779       {
10780         TBorderLinks::iterator l2b = bord->_mappedSubs[ i ];
10781         if ( l2b == borderLinks.end() || l2b->second.empty() ) continue;
10782         std::vector< FissureBorder* >& linkedBorders = l2b->second;
10783         const SubBorder&                          sb = l2b->first;
10784         const SMDS_MeshElement*             bordElem = bord->GetMarkedElem();
10785
10786         if ( linkedBorders.size() == 1 ) // fissure edge reached, fill fissEdgeNodes2Norm
10787         {
10788           for ( int j = 0; j < sb._nbNodes; ++j )
10789             fissEdgeNodes2Norm[ sb._nodes[j] ].Add( sb._nodes[j], *bord );
10790           continue;
10791         }
10792
10793         // add to theAffectedElems elems sharing nodes of a SubBorder and a node of bordElem
10794         // until an elem adjacent to a neighbour FissureBorder is found
10795         facetNodes.clear();
10796         facetNodes.insert( facetNodes.end(), sb._nodes, sb._nodes + sb._nbNodes );
10797         facetNodes.resize( sb._nbNodes + 1 );
10798
10799         while ( bordElem )
10800         {
10801           // check if bordElem is adjacent to a neighbour FissureBorder
10802           for ( size_t j = 0; j < linkedBorders.size(); ++j )
10803           {
10804             FissureBorder* bord2 = linkedBorders[j];
10805             if ( bord2 == bord ) continue;
10806             if ( bordElem == bord2->_elems[0] || bordElem == bord2->_elems[1] )
10807               bordElem = 0;
10808             else
10809               fissureNodes.insert( bord2->_nodes.begin(), bord2->_nodes.end() );
10810           }
10811           if ( !bordElem )
10812             break;
10813
10814           // find the next bordElem
10815           const SMDS_MeshElement* nextBordElem = 0;
10816           for ( int iN = 0, nbN = bordElem->NbCornerNodes(); iN < nbN  && !nextBordElem; ++iN )
10817           {
10818             const SMDS_MeshNode* n = bordElem->GetNode( iN );
10819             if ( fissureNodes.count( n )) continue;
10820
10821             facetNodes[ sb._nbNodes ] = n;
10822             elemsByFacet.clear();
10823             if ( SMDS_Mesh::GetElementsByNodes( facetNodes, elemsByFacet ) > 1 )
10824             {
10825               for ( size_t iE = 0; iE < elemsByFacet.size(); ++iE )
10826                 if ( elemsByFacet[ iE ] != bordElem &&
10827                      !elemsByFacet[ iE ]->isMarked() )
10828                 {
10829                   theAffectedElems.insert( elemsByFacet[ iE ]);
10830                   elemsByFacet[ iE ]->setIsMarked( true );
10831                   if ( elemsByFacet[ iE ]->GetType() == bordElem->GetType() )
10832                     nextBordElem = elemsByFacet[ iE ];
10833                 }
10834             }
10835           }
10836           bordElem = nextBordElem;
10837
10838         } // while ( bordElem )
10839
10840         linkedBorders.clear(); // not to treat this link any more
10841
10842       } // loop on SubBorder's of a FissureBorder
10843
10844       bord->Clear();
10845
10846     } // loop on FissureBorder's
10847
10848
10849     // add elements sharing only one node of the fissure, except those sharing fissure edge nodes
10850
10851     // mark nodes of theAffectedElems
10852     SMESH_MeshAlgos::MarkElemNodes( theAffectedElems.begin(), theAffectedElems.end(), true );
10853
10854     // unmark nodes of the fissure
10855     elIt = theElemsOrNodes.begin();
10856     if ( (*elIt)->GetType() == SMDSAbs_Node )
10857       SMESH_MeshAlgos::MarkElems( elIt, theElemsOrNodes.end(), false );
10858     else
10859       SMESH_MeshAlgos::MarkElemNodes( elIt, theElemsOrNodes.end(), false );
10860
10861     std::vector< gp_XYZ > normVec;
10862
10863     // loop on nodes of the fissure, add elements having marked nodes
10864     for ( elIt = theElemsOrNodes.begin(); elIt != theElemsOrNodes.end(); ++elIt )
10865     {
10866       const SMDS_MeshElement* e = (*elIt);
10867       if ( e->GetType() != SMDSAbs_Node )
10868         e->setIsMarked( true ); // avoid adding a fissure element
10869
10870       for ( int iN = 0, nbN = e->NbCornerNodes(); iN < nbN; ++iN )
10871       {
10872         const SMDS_MeshNode* n = e->GetNode( iN );
10873         if ( fissEdgeNodes2Norm.count( n ))
10874           continue;
10875
10876         SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
10877         while ( invIt->more() )
10878         {
10879           const SMDS_MeshElement* eInv = invIt->next();
10880           if ( eInv->isMarked() ) continue;
10881           eInv->setIsMarked( true );
10882
10883           SMDS_ElemIteratorPtr nIt = eInv->nodesIterator();
10884           while( nIt->more() )
10885             if ( nIt->next()->isMarked())
10886             {
10887               theAffectedElems.insert( eInv );
10888               SMESH_MeshAlgos::MarkElems( eInv->nodesIterator(), true );
10889               n->setIsMarked( false );
10890               break;
10891             }
10892         }
10893       }
10894     }
10895
10896     // add elements on the fissure edge
10897     std::map< const SMDS_MeshNode*, FissureNormal >::iterator n2N;
10898     for ( n2N = fissEdgeNodes2Norm.begin(); n2N != fissEdgeNodes2Norm.end(); ++n2N )
10899     {
10900       const SMDS_MeshNode* edgeNode = n2N->first;
10901       const FissureNormal & normals = n2N->second;
10902
10903       SMDS_ElemIteratorPtr invIt = edgeNode->GetInverseElementIterator();
10904       while ( invIt->more() )
10905       {
10906         const SMDS_MeshElement* eInv = invIt->next();
10907         if ( eInv->isMarked() ) continue;
10908         eInv->setIsMarked( true );
10909
10910         // classify eInv using normals
10911         bool toAdd = normals.IsIn( edgeNode, eInv );
10912         if ( toAdd ) // check if all nodes lie on the fissure edge
10913         {
10914           bool notOnEdge = false;
10915           for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN  && !notOnEdge; ++iN )
10916             notOnEdge = !fissEdgeNodes2Norm.count( eInv->GetNode( iN ));
10917           toAdd = notOnEdge;
10918         }
10919         if ( toAdd )
10920         {
10921           theAffectedElems.insert( eInv );
10922         }
10923       }
10924     }
10925
10926     return;
10927   } // findAffectedElems()
10928 } // namespace
10929
10930 //================================================================================
10931 /*!
10932  * \brief Create elements equal (on same nodes) to given ones
10933  *  \param [in] theElements - a set of elems to duplicate. If it is empty, all
10934  *              elements of the uppest dimension are duplicated.
10935  */
10936 //================================================================================
10937
10938 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
10939 {
10940   ClearLastCreated();
10941   SMESHDS_Mesh* mesh = GetMeshDS();
10942
10943   // get an element type and an iterator over elements
10944
10945   SMDSAbs_ElementType type = SMDSAbs_All;
10946   SMDS_ElemIteratorPtr elemIt;
10947   if ( theElements.empty() )
10948   {
10949     if ( mesh->NbNodes() == 0 )
10950       return;
10951     // get most complex type
10952     SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10953       SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10954       SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10955     };
10956     for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10957       if ( mesh->GetMeshInfo().NbElements( types[i] ))
10958       {
10959         type = types[i];
10960         break;
10961       }
10962     elemIt = mesh->elementsIterator( type );
10963   }
10964   else
10965   {
10966     type = (*theElements.begin())->GetType();
10967     elemIt = SMESHUtils::elemSetIterator( theElements );
10968   }
10969
10970   // un-mark all elements to avoid duplicating just created elements
10971   SMESH_MeshAlgos::MarkElems( mesh->elementsIterator( type ), false );
10972
10973   // duplicate elements
10974
10975   ElemFeatures elemType;
10976
10977   vector< const SMDS_MeshNode* > nodes;
10978   while ( elemIt->more() )
10979   {
10980     const SMDS_MeshElement* elem = elemIt->next();
10981     if ( elem->GetType() != type || elem->isMarked() )
10982       continue;
10983
10984     elemType.Init( elem, /*basicOnly=*/false );
10985     nodes.assign( elem->begin_nodes(), elem->end_nodes() );
10986
10987     if ( const SMDS_MeshElement* newElem = AddElement( nodes, elemType ))
10988       newElem->setIsMarked( true );
10989   }
10990 }
10991
10992 //================================================================================
10993 /*!
10994   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10995   \param theElems - the list of elements (edges or faces) to be replicated
10996   The nodes for duplication could be found from these elements
10997   \param theNodesNot - list of nodes to NOT replicate
10998   \param theAffectedElems - the list of elements (cells and edges) to which the
10999   replicated nodes should be associated to.
11000   \return TRUE if operation has been completed successfully, FALSE otherwise
11001 */
11002 //================================================================================
11003
11004 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
11005                                     const TIDSortedElemSet& theNodesNot,
11006                                     const TIDSortedElemSet& theAffectedElems )
11007 {
11008   ClearLastCreated();
11009
11010   if ( theElems.size() == 0 )
11011     return false;
11012
11013   SMESHDS_Mesh* aMeshDS = GetMeshDS();
11014   if ( !aMeshDS )
11015     return false;
11016
11017   bool res = false;
11018   TNodeNodeMap anOldNodeToNewNode;
11019   // duplicate elements and nodes
11020   res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
11021   // replce nodes by duplications
11022   res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
11023   return res;
11024 }
11025
11026 //================================================================================
11027 /*!
11028   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11029   \param theMeshDS - mesh instance
11030   \param theElems - the elements replicated or modified (nodes should be changed)
11031   \param theNodesNot - nodes to NOT replicate
11032   \param theNodeNodeMap - relation of old node to new created node
11033   \param theIsDoubleElem - flag os to replicate element or modify
11034   \return TRUE if operation has been completed successfully, FALSE otherwise
11035 */
11036 //================================================================================
11037
11038 bool SMESH_MeshEditor::doubleNodes(SMESHDS_Mesh*           theMeshDS,
11039                                    const TIDSortedElemSet& theElems,
11040                                    const TIDSortedElemSet& theNodesNot,
11041                                    TNodeNodeMap&           theNodeNodeMap,
11042                                    const bool              theIsDoubleElem )
11043 {
11044   // iterate through element and duplicate them (by nodes duplication)
11045   bool res = false;
11046   std::vector<const SMDS_MeshNode*> newNodes;
11047   ElemFeatures elemType;
11048
11049   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11050   for ( ;  elemItr != theElems.end(); ++elemItr )
11051   {
11052     const SMDS_MeshElement* anElem = *elemItr;
11053     // if (!anElem)
11054     //   continue;
11055
11056     // duplicate nodes to duplicate element
11057     bool isDuplicate = false;
11058     newNodes.resize( anElem->NbNodes() );
11059     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
11060     int ind = 0;
11061     while ( anIter->more() )
11062     {
11063       const SMDS_MeshNode* aCurrNode = static_cast<const SMDS_MeshNode*>( anIter->next() );
11064       const SMDS_MeshNode*  aNewNode = aCurrNode;
11065       TNodeNodeMap::iterator     n2n = theNodeNodeMap.find( aCurrNode );
11066       if ( n2n != theNodeNodeMap.end() )
11067       {
11068         aNewNode = n2n->second;
11069       }
11070       else if ( theIsDoubleElem && !theNodesNot.count( aCurrNode ))
11071       {
11072         // duplicate node
11073         aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
11074         copyPosition( aCurrNode, aNewNode );
11075         theNodeNodeMap[ aCurrNode ] = aNewNode;
11076         myLastCreatedNodes.push_back( aNewNode );
11077       }
11078       isDuplicate |= (aCurrNode != aNewNode);
11079       newNodes[ ind++ ] = aNewNode;
11080     }
11081     if ( !isDuplicate )
11082       continue;
11083
11084     if ( theIsDoubleElem )
11085       AddElement( newNodes, elemType.Init( anElem, /*basicOnly=*/false ));
11086     else
11087       theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], newNodes.size() );
11088
11089     res = true;
11090   }
11091   return res;
11092 }
11093
11094 //================================================================================
11095 /*!
11096   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11097   \param theNodes - identifiers of nodes to be doubled
11098   \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
11099   nodes. If list of element identifiers is empty then nodes are doubled but
11100   they not assigned to elements
11101   \return TRUE if operation has been completed successfully, FALSE otherwise
11102 */
11103 //================================================================================
11104
11105 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
11106                                     const std::list< int >& theListOfModifiedElems )
11107 {
11108   ClearLastCreated();
11109
11110   if ( theListOfNodes.size() == 0 )
11111     return false;
11112
11113   SMESHDS_Mesh* aMeshDS = GetMeshDS();
11114   if ( !aMeshDS )
11115     return false;
11116
11117   // iterate through nodes and duplicate them
11118
11119   std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
11120
11121   std::list< int >::const_iterator aNodeIter;
11122   for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
11123   {
11124     const SMDS_MeshNode* aNode = aMeshDS->FindNode( *aNodeIter );
11125     if ( !aNode )
11126       continue;
11127
11128     // duplicate node
11129
11130     const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
11131     if ( aNewNode )
11132     {
11133       copyPosition( aNode, aNewNode );
11134       anOldNodeToNewNode[ aNode ] = aNewNode;
11135       myLastCreatedNodes.push_back( aNewNode );
11136     }
11137   }
11138
11139   // Change nodes of elements
11140
11141   std::vector<const SMDS_MeshNode*> aNodeArr;
11142
11143   std::list< int >::const_iterator anElemIter;
11144   for ( anElemIter =  theListOfModifiedElems.begin();
11145         anElemIter != theListOfModifiedElems.end();
11146         anElemIter++ )
11147   {
11148     const SMDS_MeshElement* anElem = aMeshDS->FindElement( *anElemIter );
11149     if ( !anElem )
11150       continue;
11151
11152     aNodeArr.assign( anElem->begin_nodes(), anElem->end_nodes() );
11153     for( size_t i = 0; i < aNodeArr.size(); ++i )
11154     {
11155       std::map< const SMDS_MeshNode*, const SMDS_MeshNode* >::iterator n2n =
11156         anOldNodeToNewNode.find( aNodeArr[ i ]);
11157       if ( n2n != anOldNodeToNewNode.end() )
11158         aNodeArr[ i ] = n2n->second;
11159     }
11160     aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], aNodeArr.size() );
11161   }
11162
11163   return true;
11164 }
11165
11166 namespace {
11167
11168   //================================================================================
11169   /*!
11170     \brief Check if element located inside shape
11171     \return TRUE if IN or ON shape, FALSE otherwise
11172   */
11173   //================================================================================
11174
11175   template<class Classifier>
11176   bool isInside(const SMDS_MeshElement* theElem,
11177                 Classifier&             theClassifier,
11178                 const double            theTol)
11179   {
11180     gp_XYZ centerXYZ (0, 0, 0);
11181     SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
11182     while ( aNodeItr->more() )
11183       centerXYZ += SMESH_NodeXYZ( aNodeItr->next() );
11184
11185     gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
11186     theClassifier.Perform(aPnt, theTol);
11187     TopAbs_State aState = theClassifier.State();
11188     return (aState == TopAbs_IN || aState == TopAbs_ON );
11189   }
11190
11191   //================================================================================
11192   /*!
11193    * \brief Classifier of the 3D point on the TopoDS_Face
11194    *        with interaface suitable for isInside()
11195    */
11196   //================================================================================
11197
11198   struct _FaceClassifier
11199   {
11200     Extrema_ExtPS       _extremum;
11201     BRepAdaptor_Surface _surface;
11202     TopAbs_State        _state;
11203
11204     _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
11205     {
11206       _extremum.Initialize( _surface,
11207                             _surface.FirstUParameter(), _surface.LastUParameter(),
11208                             _surface.FirstVParameter(), _surface.LastVParameter(),
11209                             _surface.Tolerance(), _surface.Tolerance() );
11210     }
11211     void Perform(const gp_Pnt& aPnt, double theTol)
11212     {
11213       theTol *= theTol;
11214       _state = TopAbs_OUT;
11215       _extremum.Perform(aPnt);
11216       if ( _extremum.IsDone() )
11217         for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
11218           _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
11219     }
11220     TopAbs_State State() const
11221     {
11222       return _state;
11223     }
11224   };
11225 }
11226
11227 //================================================================================
11228 /*!
11229   \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
11230   This method is the first step of DoubleNodeElemGroupsInRegion.
11231   \param theElems - list of groups of elements (edges or faces) to be replicated
11232   \param theNodesNot - list of groups of nodes not to replicated
11233   \param theShape - shape to detect affected elements (element which geometric center
11234          located on or inside shape). If the shape is null, detection is done on faces orientations
11235          (select elements with a gravity center on the side given by faces normals).
11236          This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
11237          The replicated nodes should be associated to affected elements.
11238   \return true
11239   \sa DoubleNodeElemGroupsInRegion()
11240 */
11241 //================================================================================
11242
11243 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
11244                                                    const TIDSortedElemSet& theNodesNot,
11245                                                    const TopoDS_Shape&     theShape,
11246                                                    TIDSortedElemSet&       theAffectedElems)
11247 {
11248   if ( theShape.IsNull() )
11249   {
11250     findAffectedElems( theElems, theAffectedElems );
11251   }
11252   else
11253   {
11254     const double aTol = Precision::Confusion();
11255     auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
11256     auto_ptr<_FaceClassifier>              aFaceClassifier;
11257     if ( theShape.ShapeType() == TopAbs_SOLID )
11258     {
11259       bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
11260       bsc3d->PerformInfinitePoint(aTol);
11261     }
11262     else if (theShape.ShapeType() == TopAbs_FACE )
11263     {
11264       aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
11265     }
11266
11267     // iterates on indicated elements and get elements by back references from their nodes
11268     TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11269     for ( ;  elemItr != theElems.end(); ++elemItr )
11270     {
11271       SMDS_MeshElement*     anElem = (SMDS_MeshElement*)*elemItr;
11272       SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11273       while ( nodeItr->more() )
11274       {
11275         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11276         if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11277           continue;
11278         SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11279         while ( backElemItr->more() )
11280         {
11281           const SMDS_MeshElement* curElem = backElemItr->next();
11282           if ( curElem && theElems.find(curElem) == theElems.end() &&
11283                ( bsc3d.get() ?
11284                  isInside( curElem, *bsc3d, aTol ) :
11285                  isInside( curElem, *aFaceClassifier, aTol )))
11286             theAffectedElems.insert( curElem );
11287         }
11288       }
11289     }
11290   }
11291   return true;
11292 }
11293
11294 //================================================================================
11295 /*!
11296   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11297   \param theElems - group of of elements (edges or faces) to be replicated
11298   \param theNodesNot - group of nodes not to replicate
11299   \param theShape - shape to detect affected elements (element which geometric center
11300   located on or inside shape).
11301   The replicated nodes should be associated to affected elements.
11302   \return TRUE if operation has been completed successfully, FALSE otherwise
11303 */
11304 //================================================================================
11305
11306 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
11307                                             const TIDSortedElemSet& theNodesNot,
11308                                             const TopoDS_Shape&     theShape )
11309 {
11310   if ( theShape.IsNull() )
11311     return false;
11312
11313   const double aTol = Precision::Confusion();
11314   SMESHUtils::Deleter< BRepClass3d_SolidClassifier> bsc3d;
11315   SMESHUtils::Deleter<_FaceClassifier>              aFaceClassifier;
11316   if ( theShape.ShapeType() == TopAbs_SOLID )
11317   {
11318     bsc3d._obj = new BRepClass3d_SolidClassifier( theShape );
11319     bsc3d->PerformInfinitePoint(aTol);
11320   }
11321   else if (theShape.ShapeType() == TopAbs_FACE )
11322   {
11323     aFaceClassifier._obj = new _FaceClassifier( TopoDS::Face( theShape ));
11324   }
11325
11326   // iterates on indicated elements and get elements by back references from their nodes
11327   TIDSortedElemSet anAffected;
11328   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11329   for ( ;  elemItr != theElems.end(); ++elemItr )
11330   {
11331     SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11332     if (!anElem)
11333       continue;
11334
11335     SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11336     while ( nodeItr->more() )
11337     {
11338       const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11339       if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11340         continue;
11341       SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11342       while ( backElemItr->more() )
11343       {
11344         const SMDS_MeshElement* curElem = backElemItr->next();
11345         if ( curElem && theElems.find(curElem) == theElems.end() &&
11346              ( bsc3d ?
11347                isInside( curElem, *bsc3d, aTol ) :
11348                isInside( curElem, *aFaceClassifier, aTol )))
11349           anAffected.insert( curElem );
11350       }
11351     }
11352   }
11353   return DoubleNodes( theElems, theNodesNot, anAffected );
11354 }
11355
11356 /*!
11357  *  \brief compute an oriented angle between two planes defined by four points.
11358  *  The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
11359  *  @param p0 base of the rotation axe
11360  *  @param p1 extremity of the rotation axe
11361  *  @param g1 belongs to the first plane
11362  *  @param g2 belongs to the second plane
11363  */
11364 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
11365 {
11366   gp_Vec vref(p0, p1);
11367   gp_Vec v1(p0, g1);
11368   gp_Vec v2(p0, g2);
11369   gp_Vec n1 = vref.Crossed(v1);
11370   gp_Vec n2 = vref.Crossed(v2);
11371   try {
11372     return n2.AngleWithRef(n1, vref);
11373   }
11374   catch ( Standard_Failure ) {
11375   }
11376   return Max( v1.Magnitude(), v2.Magnitude() );
11377 }
11378
11379 /*!
11380  * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11381  *  The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
11382  * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
11383  * Triangles are transformed into prisms, and quadrangles into hexahedrons.
11384  * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
11385  * 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.
11386  * 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.
11387  * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
11388  * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
11389  * \param theElems - list of groups of volumes, where a group of volume is a set of
11390  *        SMDS_MeshElements sorted by Id.
11391  * \param createJointElems - if TRUE, create the elements
11392  * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
11393  *        the boundary between \a theDomains and the rest mesh
11394  * \return TRUE if operation has been completed successfully, FALSE otherwise
11395  */
11396 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11397                                                      bool                                 createJointElems,
11398                                                      bool                                 onAllBoundaries)
11399 {
11400   // MESSAGE("----------------------------------------------");
11401   // MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11402   // MESSAGE("----------------------------------------------");
11403
11404   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11405   meshDS->BuildDownWardConnectivity(true);
11406   CHRONO(50);
11407   SMDS_UnstructuredGrid *grid = meshDS->getGrid();
11408
11409   // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11410   //     build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11411   //     build the list of nodes shared by 2 or more domains, with their domain indexes
11412
11413   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11414   std::map<int,int>celldom; // cell vtkId --> domain
11415   std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains;  // oldNode --> (id domain --> id cell)
11416   std::map<int, std::map<int,int> > nodeDomains; // oldId -->  (domainId --> newId)
11417   faceDomains.clear();
11418   celldom.clear();
11419   cellDomains.clear();
11420   nodeDomains.clear();
11421   std::map<int,int> emptyMap;
11422   std::set<int> emptySet;
11423   emptyMap.clear();
11424
11425   //MESSAGE(".. Number of domains :"<<theElems.size());
11426
11427   TIDSortedElemSet theRestDomElems;
11428   const int iRestDom  = -1;
11429   const int idom0     = onAllBoundaries ? iRestDom : 0;
11430   const int nbDomains = theElems.size();
11431
11432   // Check if the domains do not share an element
11433   for (int idom = 0; idom < nbDomains-1; idom++)
11434   {
11435     //       MESSAGE("... Check of domain #" << idom);
11436     const TIDSortedElemSet& domain = theElems[idom];
11437     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11438     for (; elemItr != domain.end(); ++elemItr)
11439     {
11440       const SMDS_MeshElement* anElem = *elemItr;
11441       int idombisdeb = idom + 1 ;
11442       // check if the element belongs to a domain further in the list
11443       for ( size_t idombis = idombisdeb; idombis < theElems.size(); idombis++ )
11444       {
11445         const TIDSortedElemSet& domainbis = theElems[idombis];
11446         if ( domainbis.count( anElem ))
11447         {
11448           MESSAGE(".... Domain #" << idom);
11449           MESSAGE(".... Domain #" << idombis);
11450           throw SALOME_Exception("The domains are not disjoint.");
11451           return false ;
11452         }
11453       }
11454     }
11455   }
11456
11457   for (int idom = 0; idom < nbDomains; idom++)
11458   {
11459
11460     // --- build a map (face to duplicate --> volume to modify)
11461     //     with all the faces shared by 2 domains (group of elements)
11462     //     and corresponding volume of this domain, for each shared face.
11463     //     a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11464
11465     //MESSAGE("... Neighbors of domain #" << idom);
11466     const TIDSortedElemSet& domain = theElems[idom];
11467     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11468     for (; elemItr != domain.end(); ++elemItr)
11469     {
11470       const SMDS_MeshElement* anElem = *elemItr;
11471       if (!anElem)
11472         continue;
11473       int vtkId = anElem->getVtkId();
11474       //MESSAGE("  vtkId " << vtkId << " smdsId " << anElem->GetID());
11475       int neighborsVtkIds[NBMAXNEIGHBORS];
11476       int downIds[NBMAXNEIGHBORS];
11477       unsigned char downTypes[NBMAXNEIGHBORS];
11478       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11479       for (int n = 0; n < nbNeighbors; n++)
11480       {
11481         int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
11482         const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11483         if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
11484         {
11485           bool ok = false;
11486           for ( size_t idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
11487           {
11488             // MESSAGE("Domain " << idombis);
11489             const TIDSortedElemSet& domainbis = theElems[idombis];
11490             if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11491           }
11492           if ( ok || onAllBoundaries ) // the characteristics of the face is stored
11493           {
11494             DownIdType face(downIds[n], downTypes[n]);
11495             if (!faceDomains[face].count(idom))
11496             {
11497               faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11498               celldom[vtkId] = idom;
11499               //MESSAGE("       cell with a border " << vtkId << " domain " << idom);
11500             }
11501             if ( !ok )
11502             {
11503               theRestDomElems.insert( elem );
11504               faceDomains[face][iRestDom] = neighborsVtkIds[n];
11505               celldom[neighborsVtkIds[n]] = iRestDom;
11506             }
11507           }
11508         }
11509       }
11510     }
11511   }
11512
11513   //MESSAGE("Number of shared faces " << faceDomains.size());
11514   std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11515
11516   // --- explore the shared faces domain by domain,
11517   //     explore the nodes of the face and see if they belong to a cell in the domain,
11518   //     which has only a node or an edge on the border (not a shared face)
11519
11520   for (int idomain = idom0; idomain < nbDomains; idomain++)
11521   {
11522     //MESSAGE("Domain " << idomain);
11523     const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
11524     itface = faceDomains.begin();
11525     for (; itface != faceDomains.end(); ++itface)
11526     {
11527       const std::map<int, int>& domvol = itface->second;
11528       if (!domvol.count(idomain))
11529         continue;
11530       DownIdType face = itface->first;
11531       //MESSAGE(" --- face " << face.cellId);
11532       std::set<int> oldNodes;
11533       oldNodes.clear();
11534       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11535       std::set<int>::iterator itn = oldNodes.begin();
11536       for (; itn != oldNodes.end(); ++itn)
11537       {
11538         int oldId = *itn;
11539         //MESSAGE("     node " << oldId);
11540         vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11541         for (int i=0; i<l.ncells; i++)
11542         {
11543           int vtkId = l.cells[i];
11544           const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
11545           if (!domain.count(anElem))
11546             continue;
11547           int vtkType = grid->GetCellType(vtkId);
11548           int downId = grid->CellIdToDownId(vtkId);
11549           if (downId < 0)
11550           {
11551             MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11552             continue; // not OK at this stage of the algorithm:
11553             //no cells created after BuildDownWardConnectivity
11554           }
11555           DownIdType aCell(downId, vtkType);
11556           cellDomains[aCell][idomain] = vtkId;
11557           celldom[vtkId] = idomain;
11558           //MESSAGE("       cell " << vtkId << " domain " << idomain);
11559         }
11560       }
11561     }
11562   }
11563
11564   // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11565   //     for each shared face, get the nodes
11566   //     for each node, for each domain of the face, create a clone of the node
11567
11568   // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11569   //     junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11570   //     the value is the ordered domain ids. (more than 4 domains not taken into account)
11571
11572   std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11573   std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11574   std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11575
11576   //MESSAGE(".. Duplication of the nodes");
11577   for (int idomain = idom0; idomain < nbDomains; idomain++)
11578   {
11579     itface = faceDomains.begin();
11580     for (; itface != faceDomains.end(); ++itface)
11581     {
11582       const std::map<int, int>& domvol = itface->second;
11583       if (!domvol.count(idomain))
11584         continue;
11585       DownIdType face = itface->first;
11586       //MESSAGE(" --- face " << face.cellId);
11587       std::set<int> oldNodes;
11588       oldNodes.clear();
11589       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11590       std::set<int>::iterator itn = oldNodes.begin();
11591       for (; itn != oldNodes.end(); ++itn)
11592       {
11593         int oldId = *itn;
11594         if (nodeDomains[oldId].empty())
11595         {
11596           nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11597           //MESSAGE("-+-+-b     oldNode " << oldId << " domain " << idomain);
11598         }
11599         std::map<int, int>::const_iterator itdom = domvol.begin();
11600         for (; itdom != domvol.end(); ++itdom)
11601         {
11602           int idom = itdom->first;
11603           //MESSAGE("         domain " << idom);
11604           if (!nodeDomains[oldId].count(idom)) // --- node to clone
11605           {
11606             if (nodeDomains[oldId].size() >= 2) // a multiple node
11607             {
11608               vector<int> orderedDoms;
11609               //MESSAGE("multiple node " << oldId);
11610               if (mutipleNodes.count(oldId))
11611                 orderedDoms = mutipleNodes[oldId];
11612               else
11613               {
11614                 map<int,int>::iterator it = nodeDomains[oldId].begin();
11615                 for (; it != nodeDomains[oldId].end(); ++it)
11616                   orderedDoms.push_back(it->first);
11617               }
11618               orderedDoms.push_back(idom); // TODO order ==> push_front or back
11619               //stringstream txt;
11620               //for (int i=0; i<orderedDoms.size(); i++)
11621               //  txt << orderedDoms[i] << " ";
11622               //MESSAGE("orderedDoms " << txt.str());
11623               mutipleNodes[oldId] = orderedDoms;
11624             }
11625             double *coords = grid->GetPoint(oldId);
11626             SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11627             copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11628             int newId = newNode->getVtkId();
11629             nodeDomains[oldId][idom] = newId; // cloned node for other domains
11630             //MESSAGE("-+-+-c     oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11631           }
11632         }
11633       }
11634     }
11635   }
11636
11637   //MESSAGE(".. Creation of elements");
11638   for (int idomain = idom0; idomain < nbDomains; idomain++)
11639   {
11640     itface = faceDomains.begin();
11641     for (; itface != faceDomains.end(); ++itface)
11642     {
11643       std::map<int, int> domvol = itface->second;
11644       if (!domvol.count(idomain))
11645         continue;
11646       DownIdType face = itface->first;
11647       //MESSAGE(" --- face " << face.cellId);
11648       std::set<int> oldNodes;
11649       oldNodes.clear();
11650       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11651       int nbMultipleNodes = 0;
11652       std::set<int>::iterator itn = oldNodes.begin();
11653       for (; itn != oldNodes.end(); ++itn)
11654       {
11655         int oldId = *itn;
11656         if (mutipleNodes.count(oldId))
11657           nbMultipleNodes++;
11658       }
11659       if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11660       {
11661         //MESSAGE("multiple Nodes detected on a shared face");
11662         int downId = itface->first.cellId;
11663         unsigned char cellType = itface->first.cellType;
11664         // --- shared edge or shared face ?
11665         if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11666         {
11667           int nodes[3];
11668           int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11669           for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11670             if (mutipleNodes.count(nodes[i]))
11671               if (!mutipleNodesToFace.count(nodes[i]))
11672                 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11673         }
11674         else // shared face (between two volumes)
11675         {
11676           int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11677           const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11678           const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11679           for (int ie =0; ie < nbEdges; ie++)
11680           {
11681             int nodes[3];
11682             int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11683             if ( mutipleNodes.count(nodes[0]) && mutipleNodes.count( nodes[ nbNodes-1 ]))
11684             {
11685               vector<int> vn0 = mutipleNodes[nodes[0]];
11686               vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11687               vector<int> doms;
11688               for ( size_t i0 = 0; i0 < vn0.size(); i0++ )
11689                 for ( size_t i1 = 0; i1 < vn1.size(); i1++ )
11690                   if ( vn0[i0] == vn1[i1] )
11691                     doms.push_back( vn0[ i0 ]);
11692               if ( doms.size() > 2 )
11693               {
11694                 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11695                 double *coords = grid->GetPoint(nodes[0]);
11696                 gp_Pnt p0(coords[0], coords[1], coords[2]);
11697                 coords = grid->GetPoint(nodes[nbNodes - 1]);
11698                 gp_Pnt p1(coords[0], coords[1], coords[2]);
11699                 gp_Pnt gref;
11700                 int vtkVolIds[1000];  // an edge can belong to a lot of volumes
11701                 map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
11702                 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11703                 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11704                 for ( size_t id = 0; id < doms.size(); id++ )
11705                 {
11706                   int idom = doms[id];
11707                   const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11708                   for ( int ivol = 0; ivol < nbvol; ivol++ )
11709                   {
11710                     int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
11711                     SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
11712                     if (domain.count(elem))
11713                     {
11714                       SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
11715                       domvol[idom] = svol;
11716                       //MESSAGE("  domain " << idom << " volume " << elem->GetID());
11717                       double values[3];
11718                       vtkIdType npts = 0;
11719                       vtkIdType* pts = 0;
11720                       grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11721                       SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
11722                       if (id ==0)
11723                       {
11724                         gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
11725                         angleDom[idom] = 0;
11726                       }
11727                       else
11728                       {
11729                         gp_Pnt g(values[0], values[1], values[2]);
11730                         angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11731                         //MESSAGE("  angle=" << angleDom[idom]);
11732                       }
11733                       break;
11734                     }
11735                   }
11736                 }
11737                 map<double, int> sortedDom; // sort domains by angle
11738                 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11739                   sortedDom[ia->second] = ia->first;
11740                 vector<int> vnodes;
11741                 vector<int> vdom;
11742                 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11743                 {
11744                   vdom.push_back(ib->second);
11745                   //MESSAGE("  ordered domain " << ib->second << "  angle " << ib->first);
11746                 }
11747                 for (int ino = 0; ino < nbNodes; ino++)
11748                   vnodes.push_back(nodes[ino]);
11749                 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11750               }
11751             }
11752           }
11753         }
11754       }
11755     }
11756   }
11757
11758   // --- iterate on shared faces (volumes to modify, face to extrude)
11759   //     get node id's of the face (id SMDS = id VTK)
11760   //     create flat element with old and new nodes if requested
11761
11762   // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11763   //     (domain1 X domain2) = domain1 + MAXINT*domain2
11764
11765   std::map<int, std::map<long,int> > nodeQuadDomains;
11766   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11767
11768   //MESSAGE(".. Creation of elements: simple junction");
11769   if (createJointElems)
11770   {
11771     int idg;
11772     string joints2DName = "joints2D";
11773     mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
11774     SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11775     string joints3DName = "joints3D";
11776     mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
11777     SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11778
11779     itface = faceDomains.begin();
11780     for (; itface != faceDomains.end(); ++itface)
11781     {
11782       DownIdType face = itface->first;
11783       std::set<int> oldNodes;
11784       std::set<int>::iterator itn;
11785       oldNodes.clear();
11786       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11787
11788       std::map<int, int> domvol = itface->second;
11789       std::map<int, int>::iterator itdom = domvol.begin();
11790       int dom1 = itdom->first;
11791       int vtkVolId = itdom->second;
11792       itdom++;
11793       int dom2 = itdom->first;
11794       SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11795                                                        nodeQuadDomains);
11796       stringstream grpname;
11797       grpname << "j_";
11798       if (dom1 < dom2)
11799         grpname << dom1 << "_" << dom2;
11800       else
11801         grpname << dom2 << "_" << dom1;
11802       string namegrp = grpname.str();
11803       if (!mapOfJunctionGroups.count(namegrp))
11804         mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
11805       SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11806       if (sgrp)
11807         sgrp->Add(vol->GetID());
11808       if (vol->GetType() == SMDSAbs_Volume)
11809         joints3DGrp->Add(vol->GetID());
11810       else if (vol->GetType() == SMDSAbs_Face)
11811         joints2DGrp->Add(vol->GetID());
11812     }
11813   }
11814
11815   // --- create volumes on multiple domain intersection if requested
11816   //     iterate on mutipleNodesToFace
11817   //     iterate on edgesMultiDomains
11818
11819   //MESSAGE(".. Creation of elements: multiple junction");
11820   if (createJointElems)
11821   {
11822     // --- iterate on mutipleNodesToFace
11823
11824     std::map<int, std::vector<int> >::iterator itn =  mutipleNodesToFace.begin();
11825     for (; itn != mutipleNodesToFace.end(); ++itn)
11826     {
11827       int node = itn->first;
11828       vector<int> orderDom = itn->second;
11829       vector<vtkIdType> orderedNodes;
11830       for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11831         orderedNodes.push_back( nodeDomains[ node ][ orderDom[ idom ]]);
11832       SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11833
11834       stringstream grpname;
11835       grpname << "m2j_";
11836       grpname << 0 << "_" << 0;
11837       int idg;
11838       string namegrp = grpname.str();
11839       if (!mapOfJunctionGroups.count(namegrp))
11840         mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
11841       SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11842       if (sgrp)
11843         sgrp->Add(face->GetID());
11844     }
11845
11846     // --- iterate on edgesMultiDomains
11847
11848     std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11849     for (; ite != edgesMultiDomains.end(); ++ite)
11850     {
11851       vector<int> nodes = ite->first;
11852       vector<int> orderDom = ite->second;
11853       vector<vtkIdType> orderedNodes;
11854       if (nodes.size() == 2)
11855       {
11856         //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11857         for ( size_t ino = 0; ino < nodes.size(); ino++ )
11858           if ( orderDom.size() == 3 )
11859             for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11860               orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11861           else
11862             for (int idom = orderDom.size()-1; idom >=0; idom--)
11863               orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11864         SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11865
11866         int idg;
11867         string namegrp = "jointsMultiples";
11868         if (!mapOfJunctionGroups.count(namegrp))
11869           mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11870         SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11871         if (sgrp)
11872           sgrp->Add(vol->GetID());
11873       }
11874       else
11875       {
11876         //INFOS("Quadratic multiple joints not implemented");
11877         // TODO quadratic nodes
11878       }
11879     }
11880   }
11881
11882   // --- list the explicit faces and edges of the mesh that need to be modified,
11883   //     i.e. faces and edges built with one or more duplicated nodes.
11884   //     associate these faces or edges to their corresponding domain.
11885   //     only the first domain found is kept when a face or edge is shared
11886
11887   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11888   std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11889   faceOrEdgeDom.clear();
11890   feDom.clear();
11891
11892   //MESSAGE(".. Modification of elements");
11893   for (int idomain = idom0; idomain < nbDomains; idomain++)
11894   {
11895     std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11896     for (; itnod != nodeDomains.end(); ++itnod)
11897     {
11898       int oldId = itnod->first;
11899       //MESSAGE("     node " << oldId);
11900       vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11901       for (int i = 0; i < l.ncells; i++)
11902       {
11903         int vtkId = l.cells[i];
11904         int vtkType = grid->GetCellType(vtkId);
11905         int downId = grid->CellIdToDownId(vtkId);
11906         if (downId < 0)
11907           continue; // new cells: not to be modified
11908         DownIdType aCell(downId, vtkType);
11909         int volParents[1000];
11910         int nbvol = grid->GetParentVolumes(volParents, vtkId);
11911         for (int j = 0; j < nbvol; j++)
11912           if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11913             if (!feDom.count(vtkId))
11914             {
11915               feDom[vtkId] = idomain;
11916               faceOrEdgeDom[aCell] = emptyMap;
11917               faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11918               //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
11919               //        << " type " << vtkType << " downId " << downId);
11920             }
11921       }
11922     }
11923   }
11924
11925   // --- iterate on shared faces (volumes to modify, face to extrude)
11926   //     get node id's of the face
11927   //     replace old nodes by new nodes in volumes, and update inverse connectivity
11928
11929   std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11930   for (int m=0; m<3; m++)
11931   {
11932     std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11933     itface = (*amap).begin();
11934     for (; itface != (*amap).end(); ++itface)
11935     {
11936       DownIdType face = itface->first;
11937       std::set<int> oldNodes;
11938       std::set<int>::iterator itn;
11939       oldNodes.clear();
11940       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11941       //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11942       std::map<int, int> localClonedNodeIds;
11943
11944       std::map<int, int> domvol = itface->second;
11945       std::map<int, int>::iterator itdom = domvol.begin();
11946       for (; itdom != domvol.end(); ++itdom)
11947       {
11948         int idom = itdom->first;
11949         int vtkVolId = itdom->second;
11950         //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
11951         localClonedNodeIds.clear();
11952         for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11953         {
11954           int oldId = *itn;
11955           if (nodeDomains[oldId].count(idom))
11956           {
11957             localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11958             //MESSAGE("     node " << oldId << " --> " << localClonedNodeIds[oldId]);
11959           }
11960         }
11961         meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11962       }
11963     }
11964   }
11965
11966   // Remove empty groups (issue 0022812)
11967   std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
11968   for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
11969   {
11970     if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
11971       myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
11972   }
11973
11974   meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11975   grid->DeleteLinks();
11976
11977   CHRONOSTOP(50);
11978   counters::stats();
11979   return true;
11980 }
11981
11982 /*!
11983  * \brief Double nodes on some external faces and create flat elements.
11984  * Flat elements are mainly used by some types of mechanic calculations.
11985  *
11986  * Each group of the list must be constituted of faces.
11987  * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11988  * @param theElems - list of groups of faces, where a group of faces is a set of
11989  * SMDS_MeshElements sorted by Id.
11990  * @return TRUE if operation has been completed successfully, FALSE otherwise
11991  */
11992 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11993 {
11994   // MESSAGE("-------------------------------------------------");
11995   // MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11996   // MESSAGE("-------------------------------------------------");
11997
11998   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11999
12000   // --- For each group of faces
12001   //     duplicate the nodes, create a flat element based on the face
12002   //     replace the nodes of the faces by their clones
12003
12004   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
12005   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
12006   clonedNodes.clear();
12007   intermediateNodes.clear();
12008   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
12009   mapOfJunctionGroups.clear();
12010
12011   for ( size_t idom = 0; idom < theElems.size(); idom++ )
12012   {
12013     const TIDSortedElemSet&           domain = theElems[idom];
12014     TIDSortedElemSet::const_iterator elemItr = domain.begin();
12015     for ( ; elemItr != domain.end(); ++elemItr )
12016     {
12017       SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
12018       SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
12019       if (!aFace)
12020         continue;
12021       // MESSAGE("aFace=" << aFace->GetID());
12022       bool isQuad = aFace->IsQuadratic();
12023       vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
12024
12025       // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
12026
12027       SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
12028       while (nodeIt->more())
12029       {
12030         const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
12031         bool isMedium = isQuad && (aFace->IsMediumNode(node));
12032         if (isMedium)
12033           ln2.push_back(node);
12034         else
12035           ln0.push_back(node);
12036
12037         const SMDS_MeshNode* clone = 0;
12038         if (!clonedNodes.count(node))
12039         {
12040           clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
12041           copyPosition( node, clone );
12042           clonedNodes[node] = clone;
12043         }
12044         else
12045           clone = clonedNodes[node];
12046
12047         if (isMedium)
12048           ln3.push_back(clone);
12049         else
12050           ln1.push_back(clone);
12051
12052         const SMDS_MeshNode* inter = 0;
12053         if (isQuad && (!isMedium))
12054         {
12055           if (!intermediateNodes.count(node))
12056           {
12057             inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
12058             copyPosition( node, inter );
12059             intermediateNodes[node] = inter;
12060           }
12061           else
12062             inter = intermediateNodes[node];
12063           ln4.push_back(inter);
12064         }
12065       }
12066
12067       // --- extrude the face
12068
12069       vector<const SMDS_MeshNode*> ln;
12070       SMDS_MeshVolume* vol = 0;
12071       vtkIdType aType = aFace->GetVtkType();
12072       switch (aType)
12073       {
12074       case VTK_TRIANGLE:
12075         vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
12076         // MESSAGE("vol prism " << vol->GetID());
12077         ln.push_back(ln1[0]);
12078         ln.push_back(ln1[1]);
12079         ln.push_back(ln1[2]);
12080         break;
12081       case VTK_QUAD:
12082         vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
12083         // MESSAGE("vol hexa " << vol->GetID());
12084         ln.push_back(ln1[0]);
12085         ln.push_back(ln1[1]);
12086         ln.push_back(ln1[2]);
12087         ln.push_back(ln1[3]);
12088         break;
12089       case VTK_QUADRATIC_TRIANGLE:
12090         vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
12091                                 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
12092         // MESSAGE("vol quad prism " << vol->GetID());
12093         ln.push_back(ln1[0]);
12094         ln.push_back(ln1[1]);
12095         ln.push_back(ln1[2]);
12096         ln.push_back(ln3[0]);
12097         ln.push_back(ln3[1]);
12098         ln.push_back(ln3[2]);
12099         break;
12100       case VTK_QUADRATIC_QUAD:
12101         //              vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
12102         //                                      ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
12103         //                                      ln4[0], ln4[1], ln4[2], ln4[3]);
12104         vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
12105                                 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
12106                                 ln4[0], ln4[1], ln4[2], ln4[3]);
12107         // MESSAGE("vol quad hexa " << vol->GetID());
12108         ln.push_back(ln1[0]);
12109         ln.push_back(ln1[1]);
12110         ln.push_back(ln1[2]);
12111         ln.push_back(ln1[3]);
12112         ln.push_back(ln3[0]);
12113         ln.push_back(ln3[1]);
12114         ln.push_back(ln3[2]);
12115         ln.push_back(ln3[3]);
12116         break;
12117       case VTK_POLYGON:
12118         break;
12119       default:
12120         break;
12121       }
12122
12123       if (vol)
12124       {
12125         stringstream grpname;
12126         grpname << "jf_";
12127         grpname << idom;
12128         int idg;
12129         string namegrp = grpname.str();
12130         if (!mapOfJunctionGroups.count(namegrp))
12131           mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
12132         SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
12133         if (sgrp)
12134           sgrp->Add(vol->GetID());
12135       }
12136
12137       // --- modify the face
12138
12139       aFace->ChangeNodes(&ln[0], ln.size());
12140     }
12141   }
12142   return true;
12143 }
12144
12145 /*!
12146  *  \brief identify all the elements around a geom shape, get the faces delimiting the hole
12147  *  Build groups of volume to remove, groups of faces to replace on the skin of the object,
12148  *  groups of faces to remove inside the object, (idem edges).
12149  *  Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
12150  */
12151 void SMESH_MeshEditor::CreateHoleSkin(double                          radius,
12152                                       const TopoDS_Shape&             theShape,
12153                                       SMESH_NodeSearcher*             theNodeSearcher,
12154                                       const char*                     groupName,
12155                                       std::vector<double>&            nodesCoords,
12156                                       std::vector<std::vector<int> >& listOfListOfNodes)
12157 {
12158   // MESSAGE("--------------------------------");
12159   // MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
12160   // MESSAGE("--------------------------------");
12161
12162   // --- zone of volumes to remove is given :
12163   //     1 either by a geom shape (one or more vertices) and a radius,
12164   //     2 either by a group of nodes (representative of the shape)to use with the radius,
12165   //     3 either by a group of nodes where all the elements build on one of this nodes are to remove,
12166   //     In the case 2, the group of nodes is an external group of nodes from another mesh,
12167   //     In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
12168   //     defined by it's name.
12169
12170   SMESHDS_GroupBase* groupDS = 0;
12171   SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
12172   while ( groupIt->more() )
12173   {
12174     groupDS = 0;
12175     SMESH_Group * group = groupIt->next();
12176     if ( !group ) continue;
12177     groupDS = group->GetGroupDS();
12178     if ( !groupDS || groupDS->IsEmpty() ) continue;
12179     std::string grpName = group->GetName();
12180     //MESSAGE("grpName=" << grpName);
12181     if (grpName == groupName)
12182       break;
12183     else
12184       groupDS = 0;
12185   }
12186
12187   bool isNodeGroup = false;
12188   bool isNodeCoords = false;
12189   if (groupDS)
12190   {
12191     if (groupDS->GetType() != SMDSAbs_Node)
12192       return;
12193     isNodeGroup = true;     // a group of nodes exists and it is in this mesh
12194   }
12195
12196   if (nodesCoords.size() > 0)
12197     isNodeCoords = true; // a list o nodes given by their coordinates
12198   //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
12199
12200   // --- define groups to build
12201
12202   int idg; // --- group of SMDS volumes
12203   string grpvName = groupName;
12204   grpvName += "_vol";
12205   SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
12206   if (!grp)
12207   {
12208     MESSAGE("group not created " << grpvName);
12209     return;
12210   }
12211   SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
12212
12213   int idgs; // --- group of SMDS faces on the skin
12214   string grpsName = groupName;
12215   grpsName += "_skin";
12216   SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
12217   if (!grps)
12218   {
12219     MESSAGE("group not created " << grpsName);
12220     return;
12221   }
12222   SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
12223
12224   int idgi; // --- group of SMDS faces internal (several shapes)
12225   string grpiName = groupName;
12226   grpiName += "_internalFaces";
12227   SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
12228   if (!grpi)
12229   {
12230     MESSAGE("group not created " << grpiName);
12231     return;
12232   }
12233   SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
12234
12235   int idgei; // --- group of SMDS faces internal (several shapes)
12236   string grpeiName = groupName;
12237   grpeiName += "_internalEdges";
12238   SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
12239   if (!grpei)
12240   {
12241     MESSAGE("group not created " << grpeiName);
12242     return;
12243   }
12244   SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
12245
12246   // --- build downward connectivity
12247
12248   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
12249   meshDS->BuildDownWardConnectivity(true);
12250   SMDS_UnstructuredGrid* grid = meshDS->getGrid();
12251
12252   // --- set of volumes detected inside
12253
12254   std::set<int> setOfInsideVol;
12255   std::set<int> setOfVolToCheck;
12256
12257   std::vector<gp_Pnt> gpnts;
12258   gpnts.clear();
12259
12260   if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
12261   {
12262     //MESSAGE("group of nodes provided");
12263     SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
12264     while ( elemIt->more() )
12265     {
12266       const SMDS_MeshElement* elem = elemIt->next();
12267       if (!elem)
12268         continue;
12269       const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
12270       if (!node)
12271         continue;
12272       SMDS_MeshElement* vol = 0;
12273       SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
12274       while (volItr->more())
12275       {
12276         vol = (SMDS_MeshElement*)volItr->next();
12277         setOfInsideVol.insert(vol->getVtkId());
12278         sgrp->Add(vol->GetID());
12279       }
12280     }
12281   }
12282   else if (isNodeCoords)
12283   {
12284     //MESSAGE("list of nodes coordinates provided");
12285     size_t i = 0;
12286     int k = 0;
12287     while ( i < nodesCoords.size()-2 )
12288     {
12289       double x = nodesCoords[i++];
12290       double y = nodesCoords[i++];
12291       double z = nodesCoords[i++];
12292       gp_Pnt p = gp_Pnt(x, y ,z);
12293       gpnts.push_back(p);
12294       //MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
12295       k++;
12296     }
12297   }
12298   else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
12299   {
12300     //MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
12301     TopTools_IndexedMapOfShape vertexMap;
12302     TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
12303     gp_Pnt p = gp_Pnt(0,0,0);
12304     if (vertexMap.Extent() < 1)
12305       return;
12306
12307     for ( int i = 1; i <= vertexMap.Extent(); ++i )
12308     {
12309       const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
12310       p = BRep_Tool::Pnt(vertex);
12311       gpnts.push_back(p);
12312       //MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
12313     }
12314   }
12315
12316   if (gpnts.size() > 0)
12317   {
12318     const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
12319     //MESSAGE("startNode->nodeId " << nodeId);
12320
12321     double radius2 = radius*radius;
12322     //MESSAGE("radius2 " << radius2);
12323
12324     // --- volumes on start node
12325
12326     setOfVolToCheck.clear();
12327     SMDS_MeshElement* startVol = 0;
12328     SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
12329     while (volItr->more())
12330     {
12331       startVol = (SMDS_MeshElement*)volItr->next();
12332       setOfVolToCheck.insert(startVol->getVtkId());
12333     }
12334     if (setOfVolToCheck.empty())
12335     {
12336       MESSAGE("No volumes found");
12337       return;
12338     }
12339
12340     // --- starting with central volumes then their neighbors, check if they are inside
12341     //     or outside the domain, until no more new neighbor volume is inside.
12342     //     Fill the group of inside volumes
12343
12344     std::map<int, double> mapOfNodeDistance2;
12345     mapOfNodeDistance2.clear();
12346     std::set<int> setOfOutsideVol;
12347     while (!setOfVolToCheck.empty())
12348     {
12349       std::set<int>::iterator it = setOfVolToCheck.begin();
12350       int vtkId = *it;
12351       //MESSAGE("volume to check,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12352       bool volInside = false;
12353       vtkIdType npts = 0;
12354       vtkIdType* pts = 0;
12355       grid->GetCellPoints(vtkId, npts, pts);
12356       for (int i=0; i<npts; i++)
12357       {
12358         double distance2 = 0;
12359         if (mapOfNodeDistance2.count(pts[i]))
12360         {
12361           distance2 = mapOfNodeDistance2[pts[i]];
12362           //MESSAGE("point " << pts[i] << " distance2 " << distance2);
12363         }
12364         else
12365         {
12366           double *coords = grid->GetPoint(pts[i]);
12367           gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
12368           distance2 = 1.E40;
12369           for ( size_t j = 0; j < gpnts.size(); j++ )
12370           {
12371             double d2 = aPoint.SquareDistance( gpnts[ j ]);
12372             if (d2 < distance2)
12373             {
12374               distance2 = d2;
12375               if (distance2 < radius2)
12376                 break;
12377             }
12378           }
12379           mapOfNodeDistance2[pts[i]] = distance2;
12380           //MESSAGE("  point "  << pts[i]  << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " <<  coords[2]);
12381         }
12382         if (distance2 < radius2)
12383         {
12384           volInside = true; // one or more nodes inside the domain
12385           sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12386           break;
12387         }
12388       }
12389       if (volInside)
12390       {
12391         setOfInsideVol.insert(vtkId);
12392         //MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12393         int neighborsVtkIds[NBMAXNEIGHBORS];
12394         int downIds[NBMAXNEIGHBORS];
12395         unsigned char downTypes[NBMAXNEIGHBORS];
12396         int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12397         for (int n = 0; n < nbNeighbors; n++)
12398           if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12399             setOfVolToCheck.insert(neighborsVtkIds[n]);
12400       }
12401       else
12402       {
12403         setOfOutsideVol.insert(vtkId);
12404         //MESSAGE("  volume outside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12405       }
12406       setOfVolToCheck.erase(vtkId);
12407     }
12408   }
12409
12410   // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12411   //     If yes, add the volume to the inside set
12412
12413   bool addedInside = true;
12414   std::set<int> setOfVolToReCheck;
12415   while (addedInside)
12416   {
12417     //MESSAGE(" --------------------------- re check");
12418     addedInside = false;
12419     std::set<int>::iterator itv = setOfInsideVol.begin();
12420     for (; itv != setOfInsideVol.end(); ++itv)
12421     {
12422       int vtkId = *itv;
12423       int neighborsVtkIds[NBMAXNEIGHBORS];
12424       int downIds[NBMAXNEIGHBORS];
12425       unsigned char downTypes[NBMAXNEIGHBORS];
12426       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12427       for (int n = 0; n < nbNeighbors; n++)
12428         if (!setOfInsideVol.count(neighborsVtkIds[n]))
12429           setOfVolToReCheck.insert(neighborsVtkIds[n]);
12430     }
12431     setOfVolToCheck = setOfVolToReCheck;
12432     setOfVolToReCheck.clear();
12433     while  (!setOfVolToCheck.empty())
12434     {
12435       std::set<int>::iterator it = setOfVolToCheck.begin();
12436       int vtkId = *it;
12437       if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12438       {
12439         //MESSAGE("volume to recheck,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12440         int countInside = 0;
12441         int neighborsVtkIds[NBMAXNEIGHBORS];
12442         int downIds[NBMAXNEIGHBORS];
12443         unsigned char downTypes[NBMAXNEIGHBORS];
12444         int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12445         for (int n = 0; n < nbNeighbors; n++)
12446           if (setOfInsideVol.count(neighborsVtkIds[n]))
12447             countInside++;
12448         //MESSAGE("countInside " << countInside);
12449         if (countInside > 1)
12450         {
12451           //MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12452           setOfInsideVol.insert(vtkId);
12453           sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12454           addedInside = true;
12455         }
12456         else
12457           setOfVolToReCheck.insert(vtkId);
12458       }
12459       setOfVolToCheck.erase(vtkId);
12460     }
12461   }
12462
12463   // --- map of Downward faces at the boundary, inside the global volume
12464   //     map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12465   //     fill group of SMDS faces inside the volume (when several volume shapes)
12466   //     fill group of SMDS faces on the skin of the global volume (if skin)
12467
12468   std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12469   std::map<DownIdType, int, DownIdCompare> skinFaces;     // faces on the skin of the global volume --> corresponding cell
12470   std::set<int>::iterator it = setOfInsideVol.begin();
12471   for (; it != setOfInsideVol.end(); ++it)
12472   {
12473     int vtkId = *it;
12474     //MESSAGE("  vtkId " << vtkId  << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12475     int neighborsVtkIds[NBMAXNEIGHBORS];
12476     int downIds[NBMAXNEIGHBORS];
12477     unsigned char downTypes[NBMAXNEIGHBORS];
12478     int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12479     for (int n = 0; n < nbNeighbors; n++)
12480     {
12481       int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12482       if (neighborDim == 3)
12483       {
12484         if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12485         {
12486           DownIdType face(downIds[n], downTypes[n]);
12487           boundaryFaces[face] = vtkId;
12488         }
12489         // if the face between to volumes is in the mesh, get it (internal face between shapes)
12490         int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12491         if (vtkFaceId >= 0)
12492         {
12493           sgrpi->Add(meshDS->fromVtkToSmds(vtkFaceId));
12494           // find also the smds edges on this face
12495           int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12496           const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12497           const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12498           for (int i = 0; i < nbEdges; i++)
12499           {
12500             int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12501             if (vtkEdgeId >= 0)
12502               sgrpei->Add(meshDS->fromVtkToSmds(vtkEdgeId));
12503           }
12504         }
12505       }
12506       else if (neighborDim == 2) // skin of the volume
12507       {
12508         DownIdType face(downIds[n], downTypes[n]);
12509         skinFaces[face] = vtkId;
12510         int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12511         if (vtkFaceId >= 0)
12512           sgrps->Add(meshDS->fromVtkToSmds(vtkFaceId));
12513       }
12514     }
12515   }
12516
12517   // --- identify the edges constituting the wire of each subshape on the skin
12518   //     define polylines with the nodes of edges, equivalent to wires
12519   //     project polylines on subshapes, and partition, to get geom faces
12520
12521   std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12522   std::set<int> emptySet;
12523   emptySet.clear();
12524   std::set<int> shapeIds;
12525
12526   SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12527   while (itelem->more())
12528   {
12529     const SMDS_MeshElement *elem = itelem->next();
12530     int shapeId = elem->getshapeId();
12531     int vtkId = elem->getVtkId();
12532     if (!shapeIdToVtkIdSet.count(shapeId))
12533     {
12534       shapeIdToVtkIdSet[shapeId] = emptySet;
12535       shapeIds.insert(shapeId);
12536     }
12537     shapeIdToVtkIdSet[shapeId].insert(vtkId);
12538   }
12539
12540   std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12541   std::set<DownIdType, DownIdCompare> emptyEdges;
12542   emptyEdges.clear();
12543
12544   std::map<int, std::set<int> >::iterator itShape =  shapeIdToVtkIdSet.begin();
12545   for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12546   {
12547     int shapeId = itShape->first;
12548     //MESSAGE(" --- Shape ID --- "<< shapeId);
12549     shapeIdToEdges[shapeId] = emptyEdges;
12550
12551     std::vector<int> nodesEdges;
12552
12553     std::set<int>::iterator its = itShape->second.begin();
12554     for (; its != itShape->second.end(); ++its)
12555     {
12556       int vtkId = *its;
12557       //MESSAGE("     " << vtkId);
12558       int neighborsVtkIds[NBMAXNEIGHBORS];
12559       int downIds[NBMAXNEIGHBORS];
12560       unsigned char downTypes[NBMAXNEIGHBORS];
12561       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12562       for (int n = 0; n < nbNeighbors; n++)
12563       {
12564         if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12565           continue;
12566         int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
12567         const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12568         if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12569         {
12570           DownIdType edge(downIds[n], downTypes[n]);
12571           if (!shapeIdToEdges[shapeId].count(edge))
12572           {
12573             shapeIdToEdges[shapeId].insert(edge);
12574             int vtkNodeId[3];
12575             int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12576             nodesEdges.push_back(vtkNodeId[0]);
12577             nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12578             //MESSAGE("       --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12579           }
12580         }
12581       }
12582     }
12583
12584     std::list<int> order;
12585     order.clear();
12586     if (nodesEdges.size() > 0)
12587     {
12588       order.push_back(nodesEdges[0]); //MESSAGE("       --- back " << order.back()+1); // SMDS id = VTK id + 1;
12589       nodesEdges[0] = -1;
12590       order.push_back(nodesEdges[1]); //MESSAGE("       --- back " << order.back()+1);
12591       nodesEdges[1] = -1; // do not reuse this edge
12592       bool found = true;
12593       while (found)
12594       {
12595         int nodeTofind = order.back(); // try first to push back
12596         int i = 0;
12597         for ( i = 0; i < (int)nodesEdges.size(); i++ )
12598           if (nodesEdges[i] == nodeTofind)
12599             break;
12600         if ( i == (int) nodesEdges.size() )
12601           found = false; // no follower found on back
12602         else
12603         {
12604           if (i%2) // odd ==> use the previous one
12605             if (nodesEdges[i-1] < 0)
12606               found = false;
12607             else
12608             {
12609               order.push_back(nodesEdges[i-1]); //MESSAGE("       --- back " << order.back()+1);
12610               nodesEdges[i-1] = -1;
12611             }
12612           else // even ==> use the next one
12613             if (nodesEdges[i+1] < 0)
12614               found = false;
12615             else
12616             {
12617               order.push_back(nodesEdges[i+1]); //MESSAGE("       --- back " << order.back()+1);
12618               nodesEdges[i+1] = -1;
12619             }
12620         }
12621         if (found)
12622           continue;
12623         // try to push front
12624         found = true;
12625         nodeTofind = order.front(); // try to push front
12626         for ( i = 0;  i < (int)nodesEdges.size(); i++ )
12627           if ( nodesEdges[i] == nodeTofind )
12628             break;
12629         if ( i == (int)nodesEdges.size() )
12630         {
12631           found = false; // no predecessor found on front
12632           continue;
12633         }
12634         if (i%2) // odd ==> use the previous one
12635           if (nodesEdges[i-1] < 0)
12636             found = false;
12637           else
12638           {
12639             order.push_front(nodesEdges[i-1]); //MESSAGE("       --- front " << order.front()+1);
12640             nodesEdges[i-1] = -1;
12641           }
12642         else // even ==> use the next one
12643           if (nodesEdges[i+1] < 0)
12644             found = false;
12645           else
12646           {
12647             order.push_front(nodesEdges[i+1]); //MESSAGE("       --- front " << order.front()+1);
12648             nodesEdges[i+1] = -1;
12649           }
12650       }
12651     }
12652
12653
12654     std::vector<int> nodes;
12655     nodes.push_back(shapeId);
12656     std::list<int>::iterator itl = order.begin();
12657     for (; itl != order.end(); itl++)
12658     {
12659       nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12660       //MESSAGE("              ordered node " << nodes[nodes.size()-1]);
12661     }
12662     listOfListOfNodes.push_back(nodes);
12663   }
12664
12665   //     partition geom faces with blocFissure
12666   //     mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12667   //     mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12668
12669   return;
12670 }
12671
12672
12673 //================================================================================
12674 /*!
12675  * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12676  * The created 2D mesh elements based on nodes of free faces of boundary volumes
12677  * \return TRUE if operation has been completed successfully, FALSE otherwise
12678  */
12679 //================================================================================
12680
12681 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12682 {
12683   // iterates on volume elements and detect all free faces on them
12684   SMESHDS_Mesh* aMesh = GetMeshDS();
12685   if (!aMesh)
12686     return false;
12687
12688   ElemFeatures faceType( SMDSAbs_Face );
12689   int nbFree = 0, nbExisted = 0, nbCreated = 0;
12690   SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12691   while(vIt->more())
12692   {
12693     const SMDS_MeshVolume* volume = vIt->next();
12694     SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12695     vTool.SetExternalNormal();
12696     const int iQuad = volume->IsQuadratic();
12697     faceType.SetQuad( iQuad );
12698     for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12699     {
12700       if (!vTool.IsFreeFace(iface))
12701         continue;
12702       nbFree++;
12703       vector<const SMDS_MeshNode *> nodes;
12704       int nbFaceNodes = vTool.NbFaceNodes(iface);
12705       const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12706       int inode = 0;
12707       for ( ; inode < nbFaceNodes; inode += iQuad+1)
12708         nodes.push_back(faceNodes[inode]);
12709
12710       if (iQuad) // add medium nodes
12711       {
12712         for ( inode = 1; inode < nbFaceNodes; inode += 2)
12713           nodes.push_back(faceNodes[inode]);
12714         if ( nbFaceNodes == 9 ) // bi-quadratic quad
12715           nodes.push_back(faceNodes[8]);
12716       }
12717       // add new face based on volume nodes
12718       if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) )
12719       {
12720         nbExisted++; // face already exsist
12721       }
12722       else
12723       {
12724         AddElement( nodes, faceType.SetPoly( nbFaceNodes/(iQuad+1) > 4 ));
12725         nbCreated++;
12726       }
12727     }
12728   }
12729   return ( nbFree == ( nbExisted + nbCreated ));
12730 }
12731
12732 namespace
12733 {
12734   inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12735   {
12736     if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12737       return n;
12738     return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12739   }
12740 }
12741 //================================================================================
12742 /*!
12743  * \brief Creates missing boundary elements
12744  *  \param elements - elements whose boundary is to be checked
12745  *  \param dimension - defines type of boundary elements to create
12746  *  \param group - a group to store created boundary elements in
12747  *  \param targetMesh - a mesh to store created boundary elements in
12748  *  \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12749  *  \param toCopyExistingBoundary - if true, not only new but also pre-existing
12750  *                                boundary elements will be copied into the targetMesh
12751  *  \param toAddExistingBondary - if true, not only new but also pre-existing
12752  *                                boundary elements will be added into the new group
12753  *  \param aroundElements - if true, elements will be created on boundary of given
12754  *                          elements else, on boundary of the whole mesh.
12755  * \return nb of added boundary elements
12756  */
12757 //================================================================================
12758
12759 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12760                                        Bnd_Dimension           dimension,
12761                                        SMESH_Group*            group/*=0*/,
12762                                        SMESH_Mesh*             targetMesh/*=0*/,
12763                                        bool                    toCopyElements/*=false*/,
12764                                        bool                    toCopyExistingBoundary/*=false*/,
12765                                        bool                    toAddExistingBondary/*= false*/,
12766                                        bool                    aroundElements/*= false*/)
12767 {
12768   SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12769   SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12770   // hope that all elements are of the same type, do not check them all
12771   if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12772     throw SALOME_Exception(LOCALIZED("wrong element type"));
12773
12774   if ( !targetMesh )
12775     toCopyElements = toCopyExistingBoundary = false;
12776
12777   SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12778   SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12779   int nbAddedBnd = 0;
12780
12781   // editor adding present bnd elements and optionally holding elements to add to the group
12782   SMESH_MeshEditor* presentEditor;
12783   SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12784   presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12785
12786   SMESH_MesherHelper helper( *myMesh );
12787   const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12788   SMDS_VolumeTool vTool;
12789   TIDSortedElemSet avoidSet;
12790   const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12791   size_t inode;
12792
12793   typedef vector<const SMDS_MeshNode*> TConnectivity;
12794   TConnectivity tgtNodes;
12795   ElemFeatures elemKind( missType ), elemToCopy;
12796
12797   vector<const SMDS_MeshElement*> presentBndElems;
12798   vector<TConnectivity>           missingBndElems;
12799   vector<int>                     freeFacets;
12800   TConnectivity nodes, elemNodes;
12801
12802   SMDS_ElemIteratorPtr eIt;
12803   if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12804   else                  eIt = SMESHUtils::elemSetIterator( elements );
12805
12806   while (eIt->more())
12807   {
12808     const SMDS_MeshElement* elem = eIt->next();
12809     const int              iQuad = elem->IsQuadratic();
12810     elemKind.SetQuad( iQuad );
12811
12812     // ------------------------------------------------------------------------------------
12813     // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12814     // ------------------------------------------------------------------------------------
12815     presentBndElems.clear();
12816     missingBndElems.clear();
12817     freeFacets.clear(); nodes.clear(); elemNodes.clear();
12818     if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12819     {
12820       const SMDS_MeshElement* otherVol = 0;
12821       for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12822       {
12823         if ( !vTool.IsFreeFace(iface, &otherVol) &&
12824              ( !aroundElements || elements.count( otherVol )))
12825           continue;
12826         freeFacets.push_back( iface );
12827       }
12828       if ( missType == SMDSAbs_Face )
12829         vTool.SetExternalNormal();
12830       for ( size_t i = 0; i < freeFacets.size(); ++i )
12831       {
12832         int                iface = freeFacets[i];
12833         const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12834         const size_t nbFaceNodes = vTool.NbFaceNodes (iface);
12835         if ( missType == SMDSAbs_Edge ) // boundary edges
12836         {
12837           nodes.resize( 2+iQuad );
12838           for ( size_t i = 0; i < nbFaceNodes; i += 1+iQuad )
12839           {
12840             for ( size_t j = 0; j < nodes.size(); ++j )
12841               nodes[ j ] = nn[ i+j ];
12842             if ( const SMDS_MeshElement* edge =
12843                  aMesh->FindElement( nodes, SMDSAbs_Edge, /*noMedium=*/false ))
12844               presentBndElems.push_back( edge );
12845             else
12846               missingBndElems.push_back( nodes );
12847           }
12848         }
12849         else // boundary face
12850         {
12851           nodes.clear();
12852           for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12853             nodes.push_back( nn[inode] ); // add corner nodes
12854           if (iQuad)
12855             for ( inode = 1; inode < nbFaceNodes; inode += 2)
12856               nodes.push_back( nn[inode] ); // add medium nodes
12857           int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12858           if ( iCenter > 0 )
12859             nodes.push_back( vTool.GetNodes()[ iCenter ] );
12860
12861           if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12862                                                                SMDSAbs_Face, /*noMedium=*/false ))
12863             presentBndElems.push_back( f );
12864           else
12865             missingBndElems.push_back( nodes );
12866
12867           if ( targetMesh != myMesh )
12868           {
12869             // add 1D elements on face boundary to be added to a new mesh
12870             const SMDS_MeshElement* edge;
12871             for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12872             {
12873               if ( iQuad )
12874                 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12875               else
12876                 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12877               if ( edge && avoidSet.insert( edge ).second )
12878                 presentBndElems.push_back( edge );
12879             }
12880           }
12881         }
12882       }
12883     }
12884     else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12885     {
12886       avoidSet.clear(), avoidSet.insert( elem );
12887       elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesElemIterator() ),
12888                         SMDS_MeshElement::iterator() );
12889       elemNodes.push_back( elemNodes[0] );
12890       nodes.resize( 2 + iQuad );
12891       const int nbLinks = elem->NbCornerNodes();
12892       for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12893       {
12894         nodes[0] = elemNodes[iN];
12895         nodes[1] = elemNodes[iN+1+iQuad];
12896         if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12897           continue; // not free link
12898
12899         if ( iQuad ) nodes[2] = elemNodes[iN+1];
12900         if ( const SMDS_MeshElement* edge =
12901              aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12902           presentBndElems.push_back( edge );
12903         else
12904           missingBndElems.push_back( nodes );
12905       }
12906     }
12907
12908     // ---------------------------------
12909     // 2. Add missing boundary elements
12910     // ---------------------------------
12911     if ( targetMesh != myMesh )
12912       // instead of making a map of nodes in this mesh and targetMesh,
12913       // we create nodes with same IDs.
12914       for ( size_t i = 0; i < missingBndElems.size(); ++i )
12915       {
12916         TConnectivity& srcNodes = missingBndElems[i];
12917         tgtNodes.resize( srcNodes.size() );
12918         for ( inode = 0; inode < srcNodes.size(); ++inode )
12919           tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12920         if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( tgtNodes,
12921                                                                    missType,
12922                                                                    /*noMedium=*/false))
12923           continue;
12924         tgtEditor.AddElement( tgtNodes, elemKind.SetPoly( tgtNodes.size()/(iQuad+1) > 4 ));
12925         ++nbAddedBnd;
12926       }
12927     else
12928       for ( size_t i = 0; i < missingBndElems.size(); ++i )
12929       {
12930         TConnectivity& nodes = missingBndElems[ i ];
12931         if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12932                                                                    missType,
12933                                                                    /*noMedium=*/false))
12934           continue;
12935         SMDS_MeshElement* newElem =
12936           tgtEditor.AddElement( nodes, elemKind.SetPoly( nodes.size()/(iQuad+1) > 4 ));
12937         nbAddedBnd += bool( newElem );
12938
12939         // try to set a new element to a shape
12940         if ( myMesh->HasShapeToMesh() )
12941         {
12942           bool ok = true;
12943           set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12944           const size_t nbN = nodes.size() / (iQuad+1 );
12945           for ( inode = 0; inode < nbN && ok; ++inode )
12946           {
12947             pair<int, TopAbs_ShapeEnum> i_stype =
12948               helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12949             if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12950               mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12951           }
12952           if ( ok && mediumShapes.size() > 1 )
12953           {
12954             set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12955             pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12956             for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12957             {
12958               if (( ok = ( stype_i->first != stype_i_0.first )))
12959                 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12960                                         aMesh->IndexToShape( stype_i_0.second ));
12961             }
12962           }
12963           if ( ok && mediumShapes.begin()->first == missShapeType )
12964             aMesh->SetMeshElementOnShape( newElem, mediumShapes.begin()->second );
12965         }
12966       }
12967
12968     // ----------------------------------
12969     // 3. Copy present boundary elements
12970     // ----------------------------------
12971     if ( toCopyExistingBoundary )
12972       for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12973       {
12974         const SMDS_MeshElement* e = presentBndElems[i];
12975         tgtNodes.resize( e->NbNodes() );
12976         for ( inode = 0; inode < tgtNodes.size(); ++inode )
12977           tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12978         presentEditor->AddElement( tgtNodes, elemToCopy.Init( e ));
12979       }
12980     else // store present elements to add them to a group
12981       for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12982       {
12983         presentEditor->myLastCreatedElems.push_back( presentBndElems[ i ]);
12984       }
12985
12986   } // loop on given elements
12987
12988   // ---------------------------------------------
12989   // 4. Fill group with boundary elements
12990   // ---------------------------------------------
12991   if ( group )
12992   {
12993     if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12994       for ( size_t i = 0; i < tgtEditor.myLastCreatedElems.size(); ++i )
12995         g->SMDSGroup().Add( tgtEditor.myLastCreatedElems[ i ]);
12996   }
12997   tgtEditor.myLastCreatedElems.clear();
12998   tgtEditor2.myLastCreatedElems.clear();
12999
13000   // -----------------------
13001   // 5. Copy given elements
13002   // -----------------------
13003   if ( toCopyElements && targetMesh != myMesh )
13004   {
13005     if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
13006     else                  eIt = SMESHUtils::elemSetIterator( elements );
13007     while (eIt->more())
13008     {
13009       const SMDS_MeshElement* elem = eIt->next();
13010       tgtNodes.resize( elem->NbNodes() );
13011       for ( inode = 0; inode < tgtNodes.size(); ++inode )
13012         tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
13013       tgtEditor.AddElement( tgtNodes, elemToCopy.Init( elem ));
13014
13015       tgtEditor.myLastCreatedElems.clear();
13016     }
13017   }
13018   return nbAddedBnd;
13019 }
13020
13021 //================================================================================
13022 /*!
13023  * \brief Copy node position and set \a to node on the same geometry
13024  */
13025 //================================================================================
13026
13027 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
13028                                      const SMDS_MeshNode* to )
13029 {
13030   if ( !from || !to ) return;
13031
13032   SMDS_PositionPtr pos = from->GetPosition();
13033   if ( !pos || from->getshapeId() < 1 ) return;
13034
13035   switch ( pos->GetTypeOfPosition() )
13036   {
13037   case SMDS_TOP_3DSPACE: break;
13038
13039   case SMDS_TOP_FACE:
13040   {
13041     const SMDS_FacePosition* fPos = static_cast< const SMDS_FacePosition* >( pos );
13042     GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
13043                                 fPos->GetUParameter(), fPos->GetVParameter() );
13044     break;
13045   }
13046   case SMDS_TOP_EDGE:
13047   {
13048     // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
13049     const SMDS_EdgePosition* ePos = static_cast< const SMDS_EdgePosition* >( pos );
13050     GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
13051     break;
13052   }
13053   case SMDS_TOP_VERTEX:
13054   {
13055     GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
13056     break;
13057   }
13058   case SMDS_TOP_UNSPEC:
13059   default:;
13060   }
13061 }
13062
13063 namespace // utils for MakePolyLine
13064 {
13065   //================================================================================
13066   /*!
13067    * \brief Sequence of found points and a current point data
13068    */
13069   struct Path
13070   {
13071     std::vector< gp_XYZ >   myPoints;
13072     double                  myLength;
13073
13074     int                     mySrcPntInd; //!< start point index
13075     const SMDS_MeshElement* myFace;
13076     SMESH_NodeXYZ           myNode1;
13077     SMESH_NodeXYZ           myNode2;
13078     int                     myNodeInd1;
13079     int                     myNodeInd2;
13080     double                  myDot1;
13081     double                  myDot2;
13082     TIDSortedElemSet        myElemSet, myAvoidSet;
13083
13084     Path(): myLength(0.0), myFace(0) {}
13085
13086     bool SetCutAtCorner( const SMESH_NodeXYZ&    cornerNode,
13087                          const SMDS_MeshElement* face,
13088                          const gp_XYZ&           plnNorm,
13089                          const gp_XYZ&           plnOrig );
13090
13091     void AddPoint( const gp_XYZ& p );
13092
13093     bool Extend( const gp_XYZ& plnNorm, const gp_XYZ& plnOrig );
13094
13095     bool ReachSamePoint( const Path& other );
13096
13097     static void Remove( std::vector< Path > & paths, size_t& i );
13098   };
13099
13100   //================================================================================
13101   /*!
13102    * \brief Return true if this Path meats another
13103    */
13104   //================================================================================
13105
13106   bool Path::ReachSamePoint( const Path& other )
13107   {
13108     return ( mySrcPntInd != other.mySrcPntInd &&
13109              myFace == other.myFace );
13110   }
13111
13112   //================================================================================
13113   /*!
13114    * \brief Remove a path from a vector
13115    */
13116   //================================================================================
13117
13118   void Path::Remove( std::vector< Path > & paths, size_t& i )
13119   {
13120     if ( paths.size() > 1 )
13121     {
13122       size_t j = paths.size() - 1; // last item to be removed
13123       if ( i < j )
13124       {
13125         paths[ i ].myPoints.swap( paths[ j ].myPoints );
13126         paths[ i ].myLength    = paths[ j ].myLength;
13127         paths[ i ].mySrcPntInd = paths[ j ].mySrcPntInd;
13128         paths[ i ].myFace      = paths[ j ].myFace;
13129         paths[ i ].myNode1     = paths[ j ].myNode1;
13130         paths[ i ].myNode2     = paths[ j ].myNode2;
13131         paths[ i ].myNodeInd1  = paths[ j ].myNodeInd1;
13132         paths[ i ].myNodeInd2  = paths[ j ].myNodeInd2;
13133         paths[ i ].myDot1      = paths[ j ].myDot1;
13134         paths[ i ].myDot2      = paths[ j ].myDot2;
13135       }
13136     }
13137     paths.pop_back();
13138     if ( i > 0 )
13139       --i;
13140   }
13141
13142   //================================================================================
13143   /*!
13144    * \brief Store a point that is at a node of a face if the face is intersected by plane.
13145    *        Return false if the node is a sole intersection point of the face and the plane
13146    */
13147   //================================================================================
13148
13149   bool Path::SetCutAtCorner( const SMESH_NodeXYZ&    cornerNode,
13150                              const SMDS_MeshElement* face,
13151                              const gp_XYZ&           plnNorm,
13152                              const gp_XYZ&           plnOrig )
13153   {
13154     if ( face == myFace )
13155       return false;
13156     myNodeInd1 = face->GetNodeIndex( cornerNode._node );
13157     myNodeInd2 = ( myNodeInd1 + 1 ) % face->NbCornerNodes();
13158     int   ind3 = ( myNodeInd1 + 2 ) % face->NbCornerNodes();
13159     myNode1.Set( face->GetNode( ind3 ));
13160     myNode2.Set( face->GetNode( myNodeInd2 ));
13161
13162     myDot1 = plnNorm * ( myNode1 - plnOrig );
13163     myDot2 = plnNorm * ( myNode2 - plnOrig );
13164
13165     bool ok = ( myDot1 * myDot2 < 0 );
13166     if ( !ok && myDot1 * myDot2 == 0 )
13167     {
13168       ok = ( myDot1 != myDot2 );
13169       if ( ok && myFace )
13170         ok = ( myFace->GetNodeIndex(( myDot1 == 0 ? myNode1 : myNode2 )._node ) < 0 );
13171     }
13172     if ( ok )
13173     {
13174       myFace = face;
13175       myDot1 = 0;
13176       AddPoint( cornerNode );
13177     }
13178     return ok;
13179   }
13180
13181   //================================================================================
13182   /*!
13183    * \brief Store a point and update myLength
13184    */
13185   //================================================================================
13186
13187   void Path::AddPoint( const gp_XYZ& p )
13188   {
13189     if ( !myPoints.empty() )
13190       myLength += ( p - myPoints.back() ).Modulus();
13191     else
13192       myLength = 0;
13193     myPoints.push_back( p );
13194   }
13195
13196   //================================================================================
13197   /*!
13198    * \brief Try to find the next point
13199    *  \param [in] plnNorm - cutting plane normal
13200    *  \param [in] plnOrig - cutting plane origin
13201    */
13202   //================================================================================
13203
13204   bool Path::Extend( const gp_XYZ& plnNorm, const gp_XYZ& plnOrig )
13205   {
13206     int nodeInd3 = ( myNodeInd1 + 1 ) % myFace->NbCornerNodes();
13207     if ( myNodeInd2 == nodeInd3 )
13208       nodeInd3 = ( myNodeInd1 + 2 ) % myFace->NbCornerNodes();
13209
13210     SMESH_NodeXYZ node3 = myFace->GetNode( nodeInd3 );
13211     double         dot3 = plnNorm * ( node3 - plnOrig );
13212
13213     if ( dot3 * myDot1 < 0. )
13214     {
13215       myNode2    = node3;
13216       myNodeInd2 = nodeInd3;
13217       myDot2     = dot3;
13218     }
13219     else if ( dot3 * myDot2 < 0. )
13220     {
13221       myNode1    = node3;
13222       myNodeInd1 = nodeInd3;
13223       myDot1     = dot3;
13224     }
13225     else if ( dot3 == 0. )
13226     {
13227       SMDS_ElemIteratorPtr fIt = node3._node->GetInverseElementIterator(SMDSAbs_Face);
13228       while ( fIt->more() )
13229         if ( SetCutAtCorner( node3, fIt->next(), plnNorm, plnOrig ))
13230           return true;
13231       return false;
13232     }
13233     else if ( myDot2 == 0. )
13234     {
13235       SMESH_NodeXYZ node2 = myNode2; // copy as myNode2 changes in SetCutAtCorner()
13236       SMDS_ElemIteratorPtr fIt = node2._node->GetInverseElementIterator(SMDSAbs_Face);
13237       while ( fIt->more() )
13238         if ( SetCutAtCorner( node2, fIt->next(), plnNorm, plnOrig ))
13239           return true;
13240       return false;
13241     }
13242
13243     double r = Abs( myDot1 / ( myDot2 - myDot1 ));
13244     AddPoint( myNode1 * ( 1 - r ) + myNode2 * r );
13245
13246     myAvoidSet.clear();
13247     myAvoidSet.insert( myFace );
13248     myFace = SMESH_MeshAlgos::FindFaceInSet( myNode1._node, myNode2._node,
13249                                              myElemSet,   myAvoidSet,
13250                                              &myNodeInd1, &myNodeInd2 );
13251     return myFace;
13252   }
13253
13254   //================================================================================
13255   /*!
13256    * \brief Compute a path between two points of PolySegment
13257    */
13258   struct PolyPathCompute
13259   {
13260     SMESH_MeshEditor::TListOfPolySegments& mySegments; //!< inout PolySegment's
13261     std::vector< Path >&                   myPaths;    //!< path of each of segments to compute
13262     SMESH_Mesh*                            myMesh;
13263     mutable std::vector< std::string >     myErrors;
13264
13265     PolyPathCompute( SMESH_MeshEditor::TListOfPolySegments& theSegments,
13266                      std::vector< Path >&                   thePaths,
13267                      SMESH_Mesh*                            theMesh):
13268       mySegments( theSegments ),
13269       myPaths( thePaths ),
13270       myMesh( theMesh ),
13271       myErrors( theSegments.size() )
13272     {
13273     }
13274 #undef SMESH_CAUGHT
13275 #define SMESH_CAUGHT myErrors[i] =
13276     void operator() ( const int i ) const
13277     {
13278       SMESH_TRY;
13279       const_cast< PolyPathCompute* >( this )->Compute( i );
13280       SMESH_CATCH( SMESH::returnError );
13281     }
13282 #undef SMESH_CAUGHT
13283     //================================================================================
13284     /*!
13285      * \brief Compute a path of a given segment
13286      */
13287     //================================================================================
13288
13289     void Compute( const int iSeg )
13290     {
13291       SMESH_MeshEditor::PolySegment& polySeg = mySegments[ iSeg ];
13292
13293       // get a cutting plane
13294
13295       gp_XYZ p1 = SMESH_NodeXYZ( polySeg.myNode1[0] );
13296       gp_XYZ p2 = SMESH_NodeXYZ( polySeg.myNode1[1] );
13297       if ( polySeg.myNode2[0] ) p1 = 0.5 * ( p1 + SMESH_NodeXYZ( polySeg.myNode2[0] ));
13298       if ( polySeg.myNode2[1] ) p2 = 0.5 * ( p2 + SMESH_NodeXYZ( polySeg.myNode2[1] ));
13299
13300       gp_XYZ plnNorm = ( p1 - p2 ) ^ polySeg.myVector.XYZ();
13301       gp_XYZ plnOrig = p2;
13302
13303       // find paths connecting the 2 end points of polySeg
13304
13305       std::vector< Path > paths; paths.reserve(10);
13306
13307       // initialize paths
13308
13309       for ( int iP = 0; iP < 2; ++iP ) // loop on the polySeg end points
13310       {
13311         Path path;
13312         path.mySrcPntInd = iP;
13313         size_t nbPaths = paths.size();
13314
13315         if ( polySeg.myNode2[ iP ] && polySeg.myNode2[ iP ] != polySeg.myNode1[ iP ] )
13316         {
13317           while (( path.myFace = SMESH_MeshAlgos::FindFaceInSet( polySeg.myNode1[ iP ],
13318                                                                  polySeg.myNode2[ iP ],
13319                                                                  path.myElemSet,
13320                                                                  path.myAvoidSet,
13321                                                                  &path.myNodeInd1,
13322                                                                  &path.myNodeInd2 )))
13323           {
13324             path.myNode1.Set( polySeg.myNode1[ iP ]);
13325             path.myNode2.Set( polySeg.myNode2[ iP ]);
13326             path.myDot1 = plnNorm * ( path.myNode1 - plnOrig );
13327             path.myDot2 = plnNorm * ( path.myNode2 - plnOrig );
13328             path.myPoints.clear();
13329             path.AddPoint( 0.5 * ( path.myNode1 + path.myNode2 ));
13330             path.myAvoidSet.insert( path.myFace );
13331             paths.push_back( path );
13332           }
13333           if ( nbPaths == paths.size() )
13334             throw SALOME_Exception ( SMESH_Comment("No face edge found by point ") << iP+1
13335                                      << " in a PolySegment " << iSeg );
13336         }
13337         else // an end point is at node
13338         {
13339           std::set<const SMDS_MeshNode* > nodes;
13340           SMDS_ElemIteratorPtr fIt = polySeg.myNode1[ iP ]->GetInverseElementIterator(SMDSAbs_Face);
13341           while ( fIt->more() )
13342           {
13343             path.myPoints.clear();
13344             if ( path.SetCutAtCorner( polySeg.myNode1[ iP ], fIt->next(), plnNorm, plnOrig ))
13345             {
13346               if (( path.myDot1 * path.myDot2 != 0 ) ||
13347                   ( nodes.insert( path.myDot1 == 0 ? path.myNode1._node : path.myNode2._node ).second ))
13348                 paths.push_back( path );
13349             }
13350           }
13351         }
13352
13353         // look for a one-segment path
13354         for ( size_t i = 0; i < nbPaths; ++i )
13355           for ( size_t j = nbPaths; j < paths.size(); ++j )
13356             if ( paths[i].myFace == paths[j].myFace )
13357             {
13358               myPaths[ iSeg ].myPoints.push_back( paths[i].myPoints[0] );
13359               myPaths[ iSeg ].myPoints.push_back( paths[j].myPoints[0] );
13360               paths.clear();
13361             }
13362       }
13363
13364       // extend paths
13365
13366       myPaths[ iSeg ].myLength = 1e100;
13367
13368       while ( paths.size() >= 2 )
13369       {
13370         for ( size_t i = 0; i < paths.size(); ++i )
13371         {
13372           Path& path = paths[ i ];
13373           if ( !path.Extend( plnNorm, plnOrig ) ||         // path reached a mesh boundary
13374                path.myLength > myPaths[ iSeg ].myLength ) // path is longer than others
13375           {
13376             Path::Remove( paths, i );
13377             continue;
13378           }
13379
13380           // join paths that reach same point
13381           for ( size_t j = 0; j < paths.size(); ++j )
13382           {
13383             if ( i != j && paths[i].ReachSamePoint( paths[j] ))
13384             {
13385               double   distLast = ( paths[i].myPoints.back() - paths[j].myPoints.back() ).Modulus();
13386               double fullLength = ( paths[i].myLength + paths[j].myLength + distLast );
13387               if ( fullLength < myPaths[ iSeg ].myLength )
13388               {
13389                 myPaths[ iSeg ].myLength = fullLength;
13390                 std::vector< gp_XYZ > & allPoints = myPaths[ iSeg ].myPoints;
13391                 allPoints.swap( paths[i].myPoints );
13392                 allPoints.insert( allPoints.end(),
13393                                   paths[j].myPoints.rbegin(),
13394                                   paths[j].myPoints.rend() );
13395               }
13396               Path::Remove( paths, i );
13397               Path::Remove( paths, j );
13398             }
13399           }
13400         }
13401         if ( !paths.empty() && (int) paths[0].myPoints.size() > myMesh->NbFaces() )
13402           throw SALOME_Exception(LOCALIZED( "Infinite loop in MakePolyLine()"));
13403       }
13404
13405       if ( myPaths[ iSeg ].myPoints.empty() )
13406         throw SALOME_Exception( SMESH_Comment("Can't find a full path for PolySegment #") << iSeg );
13407
13408     } // PolyPathCompute::Compute()
13409
13410   }; // struct PolyPathCompute
13411
13412 } // namespace
13413
13414 //=======================================================================
13415 //function : MakePolyLine
13416 //purpose  : Create a polyline consisting of 1D mesh elements each lying on a 2D element of
13417 //           the initial mesh
13418 //=======================================================================
13419
13420 void SMESH_MeshEditor::MakePolyLine( TListOfPolySegments&   theSegments,
13421                                      SMESHDS_Group*         theGroup,
13422                                      SMESH_ElementSearcher* theSearcher)
13423 {
13424   std::vector< Path > segPaths( theSegments.size() ); // path of each of segments
13425
13426   SMESH_ElementSearcher* searcher = theSearcher;
13427   SMESHUtils::Deleter<SMESH_ElementSearcher> delSearcher;
13428   if ( !searcher )
13429   {
13430     searcher = SMESH_MeshAlgos::GetElementSearcher( *GetMeshDS() );
13431     delSearcher._obj = searcher;
13432   }
13433
13434   // get cutting planes
13435
13436   std::vector< bool > isVectorOK( theSegments.size(), true );
13437   const double planarCoef = 0.333; // plane height in planar case
13438
13439   for ( size_t iSeg = 0; iSeg < theSegments.size(); ++iSeg )
13440   {
13441     PolySegment& polySeg = theSegments[ iSeg ];
13442
13443     gp_XYZ p1 = SMESH_NodeXYZ( polySeg.myNode1[0] );
13444     gp_XYZ p2 = SMESH_NodeXYZ( polySeg.myNode1[1] );
13445     if ( polySeg.myNode2[0] ) p1 = 0.5 * ( p1 + SMESH_NodeXYZ( polySeg.myNode2[0] ));
13446     if ( polySeg.myNode2[1] ) p2 = 0.5 * ( p2 + SMESH_NodeXYZ( polySeg.myNode2[1] ));
13447
13448     gp_XYZ plnNorm = ( p1 - p2 ) ^ polySeg.myVector.XYZ();
13449
13450     isVectorOK[ iSeg ] = ( plnNorm.Modulus() > std::numeric_limits<double>::min() );
13451     if ( !isVectorOK[ iSeg ])
13452     {
13453       gp_XYZ pMid = 0.5 * ( p1 + p2 );
13454       const SMDS_MeshElement* face;
13455       polySeg.myMidProjPoint = searcher->Project( pMid, SMDSAbs_Face, &face );
13456       polySeg.myVector       = polySeg.myMidProjPoint.XYZ() - pMid;
13457
13458       gp_XYZ faceNorm;
13459       SMESH_MeshAlgos::FaceNormal( face, faceNorm );
13460
13461       if ( polySeg.myVector.Magnitude() < Precision::Confusion() ||
13462            polySeg.myVector * faceNorm  < Precision::Confusion() )
13463       {
13464         polySeg.myVector = faceNorm;
13465         polySeg.myMidProjPoint = pMid + faceNorm * ( p1 - p2 ).Modulus() * planarCoef;
13466       }
13467     }
13468     else
13469     {
13470       polySeg.myVector = plnNorm ^ ( p1 - p2 );
13471     }
13472   }
13473
13474   // assure that inverse elements are constructed, avoid their concurrent building in threads
13475   GetMeshDS()->nodesIterator()->next()->NbInverseElements();
13476
13477   // find paths
13478
13479   PolyPathCompute algo( theSegments, segPaths, myMesh );
13480   OSD_Parallel::For( 0, theSegments.size(), algo, theSegments.size() == 1 );
13481
13482   for ( size_t iSeg = 0; iSeg < theSegments.size(); ++iSeg )
13483     if ( !algo.myErrors[ iSeg ].empty() )
13484       throw SALOME_Exception( algo.myErrors[ iSeg ].c_str() );
13485
13486   // create an 1D mesh
13487
13488   const SMDS_MeshNode *n, *nPrev = 0;
13489   SMESHDS_Mesh* mesh = GetMeshDS();
13490
13491   for ( size_t iSeg = 0; iSeg < theSegments.size(); ++iSeg )
13492   {
13493     const Path& path = segPaths[iSeg];
13494     if ( path.myPoints.size() < 2 )
13495       continue;
13496
13497     double tol = path.myLength / path.myPoints.size() / 1000.;
13498     if ( !nPrev || ( SMESH_NodeXYZ( nPrev ) - path.myPoints[0] ).SquareModulus() > tol*tol )
13499     {
13500       nPrev = mesh->AddNode( path.myPoints[0].X(), path.myPoints[0].Y(), path.myPoints[0].Z() );
13501       myLastCreatedNodes.push_back( nPrev );
13502     }
13503     for ( size_t iP = 1; iP < path.myPoints.size(); ++iP )
13504     {
13505       n = mesh->AddNode( path.myPoints[iP].X(), path.myPoints[iP].Y(), path.myPoints[iP].Z() );
13506       myLastCreatedNodes.push_back( n );
13507
13508       const SMDS_MeshElement* elem = mesh->AddEdge( nPrev, n );
13509       myLastCreatedElems.push_back( elem );
13510       if ( theGroup )
13511         theGroup->Add( elem );
13512
13513       nPrev = n;
13514     }
13515
13516     // return a vector
13517
13518     gp_XYZ pMid = 0.5 * ( path.myPoints[0] + path.myPoints.back() );
13519     if ( isVectorOK[ iSeg ])
13520     {
13521       // find the most distance point of a path
13522       double maxDist = 0;
13523       for ( size_t iP = 1; iP < path.myPoints.size(); ++iP )
13524       {
13525         double dist = Abs( theSegments[iSeg].myVector * ( path.myPoints[iP] - path.myPoints[0] ));
13526         if ( dist > maxDist )
13527         {
13528           maxDist = dist;
13529           theSegments[iSeg].myMidProjPoint = path.myPoints[iP];
13530         }
13531       }
13532       if ( maxDist < Precision::Confusion() ) // planar case
13533         theSegments[iSeg].myMidProjPoint =
13534           pMid + theSegments[iSeg].myVector.XYZ().Normalized() * path.myLength * planarCoef;
13535     }
13536     theSegments[iSeg].myVector = gp_Vec( pMid, theSegments[iSeg].myMidProjPoint );
13537   }
13538
13539   return;
13540 }