Salome HOME
Fix regression of SALOME_TESTS/Grids/smesh/3D_mesh_Extrusion_01/B2
[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 <Basics_OCCTVersion.hxx>
50
51 #include "utilities.h"
52 #include "chrono.hxx"
53
54 #include <BRepAdaptor_Surface.hxx>
55 #include <BRepBuilderAPI_MakeEdge.hxx>
56 #include <BRepClass3d_SolidClassifier.hxx>
57 #include <BRep_Tool.hxx>
58 #include <ElCLib.hxx>
59 #include <Extrema_GenExtPS.hxx>
60 #include <Extrema_POnCurv.hxx>
61 #include <Extrema_POnSurf.hxx>
62 #include <Geom2d_Curve.hxx>
63 #include <GeomAdaptor_Surface.hxx>
64 #include <Geom_Curve.hxx>
65 #include <Geom_Surface.hxx>
66 #include <Precision.hxx>
67 #include <TColStd_ListOfInteger.hxx>
68 #include <TopAbs_State.hxx>
69 #include <TopExp.hxx>
70 #include <TopExp_Explorer.hxx>
71 #include <TopTools_ListIteratorOfListOfShape.hxx>
72 #include <TopTools_ListOfShape.hxx>
73 #include <TopTools_SequenceOfShape.hxx>
74 #include <TopoDS.hxx>
75 #include <TopoDS_Edge.hxx>
76 #include <TopoDS_Face.hxx>
77 #include <TopoDS_Solid.hxx>
78 #include <gp.hxx>
79 #include <gp_Ax1.hxx>
80 #include <gp_Dir.hxx>
81 #include <gp_Lin.hxx>
82 #include <gp_Pln.hxx>
83 #include <gp_Trsf.hxx>
84 #include <gp_Vec.hxx>
85 #include <gp_XY.hxx>
86 #include <gp_XYZ.hxx>
87
88 #include <cmath>
89
90 #include <map>
91 #include <set>
92 #include <numeric>
93 #include <limits>
94 #include <algorithm>
95 #include <sstream>
96
97 #include <boost/tuple/tuple.hpp>
98
99 #include <Standard_Failure.hxx>
100 #include <Standard_ErrorHandler.hxx>
101
102 #define cast2Node(elem) static_cast<const SMDS_MeshNode*>( elem )
103
104 using namespace std;
105 using namespace SMESH::Controls;
106
107 namespace
108 {
109   template < class ELEM_SET >
110   SMDS_ElemIteratorPtr elemSetIterator( const ELEM_SET& elements )
111   {
112     typedef SMDS_SetIterator
113       < SMDS_pElement, typename ELEM_SET::const_iterator> TSetIterator;
114     return SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
115   }
116 }
117
118 //=======================================================================
119 //function : SMESH_MeshEditor
120 //purpose  :
121 //=======================================================================
122
123 SMESH_MeshEditor::SMESH_MeshEditor( SMESH_Mesh* theMesh )
124   :myMesh( theMesh ) // theMesh may be NULL
125 {
126 }
127
128 //================================================================================
129 /*!
130  * \brief Return mesh DS
131  */
132 //================================================================================
133
134 SMESHDS_Mesh * SMESH_MeshEditor::GetMeshDS()
135 {
136   return myMesh->GetMeshDS();
137 }
138
139
140 //================================================================================
141 /*!
142  * \brief Clears myLastCreatedNodes and myLastCreatedElems
143  */
144 //================================================================================
145
146 void SMESH_MeshEditor::ClearLastCreated()
147 {
148   myLastCreatedNodes.Clear();
149   myLastCreatedElems.Clear();
150 }
151
152 //================================================================================
153 /*!
154  * \brief Initializes members by an existing element
155  *  \param [in] elem - the source element
156  *  \param [in] basicOnly - if true, does not set additional data of Ball and Polyhedron
157  */
158 //================================================================================
159
160 SMESH_MeshEditor::ElemFeatures&
161 SMESH_MeshEditor::ElemFeatures::Init( const SMDS_MeshElement* elem, bool basicOnly )
162 {
163   if ( elem )
164   {
165     myType = elem->GetType();
166     if ( myType == SMDSAbs_Face || myType == SMDSAbs_Volume )
167     {
168       myIsPoly = elem->IsPoly();
169       if ( myIsPoly )
170       {
171         myIsQuad = elem->IsQuadratic();
172         if ( myType == SMDSAbs_Volume && !basicOnly )
173         {
174           vector<int > quant = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
175           myPolyhedQuantities.swap( quant );
176         }
177       }
178     }
179     else if ( myType == SMDSAbs_Ball && !basicOnly )
180     {
181       myBallDiameter = static_cast<const SMDS_BallElement*>(elem)->GetDiameter();
182     }
183   }
184   return *this;
185 }
186
187 //=======================================================================
188 /*!
189  * \brief Add element
190  */
191 //=======================================================================
192
193 SMDS_MeshElement*
194 SMESH_MeshEditor::AddElement(const vector<const SMDS_MeshNode*> & node,
195                              const ElemFeatures&                  features)
196 {
197   SMDS_MeshElement* e = 0;
198   int nbnode = node.size();
199   SMESHDS_Mesh* mesh = GetMeshDS();
200   const int ID = features.myID;
201
202   switch ( features.myType ) {
203   case SMDSAbs_Face:
204     if ( !features.myIsPoly ) {
205       if      (nbnode == 3) {
206         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], ID);
207         else           e = mesh->AddFace      (node[0], node[1], node[2] );
208       }
209       else if (nbnode == 4) {
210         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], ID);
211         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3] );
212       }
213       else if (nbnode == 6) {
214         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
215                                                node[4], node[5], ID);
216         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
217                                                node[4], node[5] );
218       }
219       else if (nbnode == 7) {
220         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
221                                                node[4], node[5], node[6], ID);
222         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
223                                                node[4], node[5], node[6] );
224       }
225       else if (nbnode == 8) {
226         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
227                                                node[4], node[5], node[6], node[7], ID);
228         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
229                                                node[4], node[5], node[6], node[7] );
230       }
231       else if (nbnode == 9) {
232         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
233                                                node[4], node[5], node[6], node[7], node[8], ID);
234         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
235                                                node[4], node[5], node[6], node[7], node[8] );
236       }
237     }
238     else if ( !features.myIsQuad )
239     {
240       if ( ID >= 1 ) e = mesh->AddPolygonalFaceWithID(node, ID);
241       else           e = mesh->AddPolygonalFace      (node    );
242     }
243     else if ( nbnode % 2 == 0 ) // just a protection
244     {
245       if ( ID >= 1 ) e = mesh->AddQuadPolygonalFaceWithID(node, ID);
246       else           e = mesh->AddQuadPolygonalFace      (node    );
247     }
248     break;
249
250   case SMDSAbs_Volume:
251     if ( !features.myIsPoly ) {
252       if      (nbnode == 4) {
253         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], ID);
254         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3] );
255       }
256       else if (nbnode == 5) {
257         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
258                                                  node[4], ID);
259         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
260                                                  node[4] );
261       }
262       else if (nbnode == 6) {
263         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
264                                                  node[4], node[5], ID);
265         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
266                                                  node[4], node[5] );
267       }
268       else if (nbnode == 8) {
269         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
270                                                  node[4], node[5], node[6], node[7], ID);
271         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
272                                                  node[4], node[5], node[6], node[7] );
273       }
274       else if (nbnode == 10) {
275         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
276                                                  node[4], node[5], node[6], node[7],
277                                                  node[8], node[9], ID);
278         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
279                                                  node[4], node[5], node[6], node[7],
280                                                  node[8], node[9] );
281       }
282       else if (nbnode == 12) {
283         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
284                                                  node[4], node[5], node[6], node[7],
285                                                  node[8], node[9], node[10], node[11], 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       }
290       else if (nbnode == 13) {
291         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
292                                                  node[4], node[5], node[6], node[7],
293                                                  node[8], node[9], node[10],node[11],
294                                                  node[12],ID);
295         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
296                                                  node[4], node[5], node[6], node[7],
297                                                  node[8], node[9], node[10],node[11],
298                                                  node[12] );
299       }
300       else if (nbnode == 15) {
301         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
302                                                  node[4], node[5], node[6], node[7],
303                                                  node[8], node[9], node[10],node[11],
304                                                  node[12],node[13],node[14],ID);
305         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
306                                                  node[4], node[5], node[6], node[7],
307                                                  node[8], node[9], node[10],node[11],
308                                                  node[12],node[13],node[14] );
309       }
310       else if (nbnode == 20) {
311         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
312                                                  node[4], node[5], node[6], node[7],
313                                                  node[8], node[9], node[10],node[11],
314                                                  node[12],node[13],node[14],node[15],
315                                                  node[16],node[17],node[18],node[19],ID);
316         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
317                                                  node[4], node[5], node[6], node[7],
318                                                  node[8], node[9], node[10],node[11],
319                                                  node[12],node[13],node[14],node[15],
320                                                  node[16],node[17],node[18],node[19] );
321       }
322       else if (nbnode == 27) {
323         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
324                                                  node[4], node[5], node[6], node[7],
325                                                  node[8], node[9], node[10],node[11],
326                                                  node[12],node[13],node[14],node[15],
327                                                  node[16],node[17],node[18],node[19],
328                                                  node[20],node[21],node[22],node[23],
329                                                  node[24],node[25],node[26], ID);
330         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
331                                                  node[4], node[5], node[6], node[7],
332                                                  node[8], node[9], node[10],node[11],
333                                                  node[12],node[13],node[14],node[15],
334                                                  node[16],node[17],node[18],node[19],
335                                                  node[20],node[21],node[22],node[23],
336                                                  node[24],node[25],node[26] );
337       }
338     }
339     else if ( !features.myIsQuad )
340     {
341       if ( ID >= 1 ) e = mesh->AddPolyhedralVolumeWithID(node, features.myPolyhedQuantities, ID);
342       else           e = mesh->AddPolyhedralVolume      (node, features.myPolyhedQuantities    );
343     }
344     else
345     {
346       // if ( ID >= 1 ) e = mesh->AddQuadPolyhedralVolumeWithID(node, features.myPolyhedQuantities,ID);
347       // else           e = mesh->AddQuadPolyhedralVolume      (node, features.myPolyhedQuantities   );
348     }
349     break;
350
351   case SMDSAbs_Edge:
352     if ( nbnode == 2 ) {
353       if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], ID);
354       else           e = mesh->AddEdge      (node[0], node[1] );
355     }
356     else if ( nbnode == 3 ) {
357       if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], node[2], ID);
358       else           e = mesh->AddEdge      (node[0], node[1], node[2] );
359     }
360     break;
361
362   case SMDSAbs_0DElement:
363     if ( nbnode == 1 ) {
364       if ( ID >= 1 ) e = mesh->Add0DElementWithID(node[0], ID);
365       else           e = mesh->Add0DElement      (node[0] );
366     }
367     break;
368
369   case SMDSAbs_Node:
370     if ( ID >= 1 ) e = mesh->AddNodeWithID(node[0]->X(), node[0]->Y(), node[0]->Z(), ID);
371     else           e = mesh->AddNode      (node[0]->X(), node[0]->Y(), node[0]->Z()    );
372     break;
373
374   case SMDSAbs_Ball:
375     if ( ID >= 1 ) e = mesh->AddBallWithID(node[0], features.myBallDiameter, ID);
376     else           e = mesh->AddBall      (node[0], features.myBallDiameter    );
377     break;
378
379   default:;
380   }
381   if ( e ) myLastCreatedElems.Append( e );
382   return e;
383 }
384
385 //=======================================================================
386 /*!
387  * \brief Add element
388  */
389 //=======================================================================
390
391 SMDS_MeshElement* SMESH_MeshEditor::AddElement(const vector<int> & nodeIDs,
392                                                const ElemFeatures& features)
393 {
394   vector<const SMDS_MeshNode*> nodes;
395   nodes.reserve( nodeIDs.size() );
396   vector<int>::const_iterator id = nodeIDs.begin();
397   while ( id != nodeIDs.end() ) {
398     if ( const SMDS_MeshNode* node = GetMeshDS()->FindNode( *id++ ))
399       nodes.push_back( node );
400     else
401       return 0;
402   }
403   return AddElement( nodes, features );
404 }
405
406 //=======================================================================
407 //function : Remove
408 //purpose  : Remove a node or an element.
409 //           Modify a compute state of sub-meshes which become empty
410 //=======================================================================
411
412 int SMESH_MeshEditor::Remove (const list< int >& theIDs,
413                               const bool         isNodes )
414 {
415   myLastCreatedElems.Clear();
416   myLastCreatedNodes.Clear();
417
418   SMESHDS_Mesh* aMesh = GetMeshDS();
419   set< SMESH_subMesh *> smmap;
420
421   int removed = 0;
422   list<int>::const_iterator it = theIDs.begin();
423   for ( ; it != theIDs.end(); it++ ) {
424     const SMDS_MeshElement * elem;
425     if ( isNodes )
426       elem = aMesh->FindNode( *it );
427     else
428       elem = aMesh->FindElement( *it );
429     if ( !elem )
430       continue;
431
432     // Notify VERTEX sub-meshes about modification
433     if ( isNodes ) {
434       const SMDS_MeshNode* node = cast2Node( elem );
435       if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX )
436         if ( int aShapeID = node->getshapeId() )
437           if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
438             smmap.insert( sm );
439     }
440     // Find sub-meshes to notify about modification
441     //     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
442     //     while ( nodeIt->more() ) {
443     //       const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
444     //       const SMDS_PositionPtr& aPosition = node->GetPosition();
445     //       if ( aPosition.get() ) {
446     //         if ( int aShapeID = aPosition->GetShapeId() ) {
447     //           if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
448     //             smmap.insert( sm );
449     //         }
450     //       }
451     //     }
452
453     // Do remove
454     if ( isNodes )
455       aMesh->RemoveNode( static_cast< const SMDS_MeshNode* >( elem ));
456     else
457       aMesh->RemoveElement( elem );
458     removed++;
459   }
460
461   // Notify sub-meshes about modification
462   if ( !smmap.empty() ) {
463     set< SMESH_subMesh *>::iterator smIt;
464     for ( smIt = smmap.begin(); smIt != smmap.end(); smIt++ )
465       (*smIt)->ComputeStateEngine( SMESH_subMesh::MESH_ENTITY_REMOVED );
466   }
467
468   //   // Check if the whole mesh becomes empty
469   //   if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( 1 ) )
470   //     sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
471
472   return removed;
473 }
474
475 //================================================================================
476 /*!
477  * \brief Create 0D elements on all nodes of the given object.
478  *  \param elements - Elements on whose nodes to create 0D elements; if empty, 
479  *                    the all mesh is treated
480  *  \param all0DElems - returns all 0D elements found or created on nodes of \a elements
481  *  \param duplicateElements - to add one more 0D element to a node or not
482  */
483 //================================================================================
484
485 void SMESH_MeshEditor::Create0DElementsOnAllNodes( const TIDSortedElemSet& elements,
486                                                    TIDSortedElemSet&       all0DElems,
487                                                    const bool              duplicateElements )
488 {
489   SMDS_ElemIteratorPtr elemIt;
490   if ( elements.empty() )
491   {
492     elemIt = GetMeshDS()->elementsIterator( SMDSAbs_Node );
493   }
494   else
495   {
496     elemIt = elemSetIterator( elements );
497   }
498
499   while ( elemIt->more() )
500   {
501     const SMDS_MeshElement* e = elemIt->next();
502     SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
503     while ( nodeIt->more() )
504     {
505       const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
506       SMDS_ElemIteratorPtr it0D = n->GetInverseElementIterator( SMDSAbs_0DElement );
507       if ( duplicateElements || !it0D->more() )
508       {
509         myLastCreatedElems.Append( GetMeshDS()->Add0DElement( n ));
510         all0DElems.insert( myLastCreatedElems.Last() );
511       }
512       while ( it0D->more() )
513         all0DElems.insert( it0D->next() );
514     }
515   }
516 }
517
518 //=======================================================================
519 //function : FindShape
520 //purpose  : Return an index of the shape theElem is on
521 //           or zero if a shape not found
522 //=======================================================================
523
524 int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem)
525 {
526   myLastCreatedElems.Clear();
527   myLastCreatedNodes.Clear();
528
529   SMESHDS_Mesh * aMesh = GetMeshDS();
530   if ( aMesh->ShapeToMesh().IsNull() )
531     return 0;
532
533   int aShapeID = theElem->getshapeId();
534   if ( aShapeID < 1 )
535     return 0;
536
537   if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ))
538     if ( sm->Contains( theElem ))
539       return aShapeID;
540
541   if ( theElem->GetType() == SMDSAbs_Node ) {
542     MESSAGE( ":( Error: invalid myShapeId of node " << theElem->GetID() );
543   }
544   else {
545     MESSAGE( ":( Error: invalid myShapeId of element " << theElem->GetID() );
546   }
547
548   TopoDS_Shape aShape; // the shape a node of theElem is on
549   if ( theElem->GetType() != SMDSAbs_Node )
550   {
551     SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
552     while ( nodeIt->more() ) {
553       const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
554       if ((aShapeID = node->getshapeId()) > 0) {
555         if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ) ) {
556           if ( sm->Contains( theElem ))
557             return aShapeID;
558           if ( aShape.IsNull() )
559             aShape = aMesh->IndexToShape( aShapeID );
560         }
561       }
562     }
563   }
564
565   // None of nodes is on a proper shape,
566   // find the shape among ancestors of aShape on which a node is
567   if ( !aShape.IsNull() ) {
568     TopTools_ListIteratorOfListOfShape ancIt( GetMesh()->GetAncestors( aShape ));
569     for ( ; ancIt.More(); ancIt.Next() ) {
570       SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() );
571       if ( sm && sm->Contains( theElem ))
572         return aMesh->ShapeToIndex( ancIt.Value() );
573     }
574   }
575   else
576   {
577     SMESHDS_SubMeshIteratorPtr smIt = GetMeshDS()->SubMeshes();
578     while ( const SMESHDS_SubMesh* sm = smIt->next() )
579       if ( sm->Contains( theElem ))
580         return sm->GetID();
581   }
582
583   return 0;
584 }
585
586 //=======================================================================
587 //function : IsMedium
588 //purpose  :
589 //=======================================================================
590
591 bool SMESH_MeshEditor::IsMedium(const SMDS_MeshNode*      node,
592                                 const SMDSAbs_ElementType typeToCheck)
593 {
594   bool isMedium = false;
595   SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(typeToCheck);
596   while (it->more() && !isMedium ) {
597     const SMDS_MeshElement* elem = it->next();
598     isMedium = elem->IsMediumNode(node);
599   }
600   return isMedium;
601 }
602
603 //=======================================================================
604 //function : shiftNodesQuadTria
605 //purpose  : Shift nodes in the array corresponded to quadratic triangle
606 //           example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
607 //=======================================================================
608
609 static void shiftNodesQuadTria(vector< const SMDS_MeshNode* >& aNodes)
610 {
611   const SMDS_MeshNode* nd1 = aNodes[0];
612   aNodes[0] = aNodes[1];
613   aNodes[1] = aNodes[2];
614   aNodes[2] = nd1;
615   const SMDS_MeshNode* nd2 = aNodes[3];
616   aNodes[3] = aNodes[4];
617   aNodes[4] = aNodes[5];
618   aNodes[5] = nd2;
619 }
620
621 //=======================================================================
622 //function : nbEdgeConnectivity
623 //purpose  : return number of the edges connected with the theNode.
624 //           if theEdges has connections with the other type of the
625 //           elements, return -1
626 //=======================================================================
627
628 static int nbEdgeConnectivity(const SMDS_MeshNode* theNode)
629 {
630   // SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator();
631   // int nb=0;
632   // while(elemIt->more()) {
633   //   elemIt->next();
634   //   nb++;
635   // }
636   // return nb;
637   return theNode->NbInverseElements();
638 }
639
640 //=======================================================================
641 //function : getNodesFromTwoTria
642 //purpose  : 
643 //=======================================================================
644
645 static bool getNodesFromTwoTria(const SMDS_MeshElement * theTria1,
646                                 const SMDS_MeshElement * theTria2,
647                                 vector< const SMDS_MeshNode*>& N1,
648                                 vector< const SMDS_MeshNode*>& N2)
649 {
650   N1.assign( theTria1->begin_nodes(), theTria1->end_nodes() );
651   if ( N1.size() < 6 ) return false;
652   N2.assign( theTria2->begin_nodes(), theTria2->end_nodes() );
653   if ( N2.size() < 6 ) return false;
654
655   int sames[3] = {-1,-1,-1};
656   int nbsames = 0;
657   int i, j;
658   for(i=0; i<3; i++) {
659     for(j=0; j<3; j++) {
660       if(N1[i]==N2[j]) {
661         sames[i] = j;
662         nbsames++;
663         break;
664       }
665     }
666   }
667   if(nbsames!=2) return false;
668   if(sames[0]>-1) {
669     shiftNodesQuadTria(N1);
670     if(sames[1]>-1) {
671       shiftNodesQuadTria(N1);
672     }
673   }
674   i = sames[0] + sames[1] + sames[2];
675   for(; i<2; i++) {
676     shiftNodesQuadTria(N2);
677   }
678   // now we receive following N1 and N2 (using numeration as in the image below)
679   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
680   // i.e. first nodes from both arrays form a new diagonal
681   return true;
682 }
683
684 //=======================================================================
685 //function : InverseDiag
686 //purpose  : Replace two neighbour triangles with ones built on the same 4 nodes
687 //           but having other common link.
688 //           Return False if args are improper
689 //=======================================================================
690
691 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1,
692                                     const SMDS_MeshElement * theTria2 )
693 {
694   myLastCreatedElems.Clear();
695   myLastCreatedNodes.Clear();
696
697   if (!theTria1 || !theTria2)
698     return false;
699
700   const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( theTria1 );
701   if (!F1) return false;
702   const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( theTria2 );
703   if (!F2) return false;
704   if ((theTria1->GetEntityType() == SMDSEntity_Triangle) &&
705       (theTria2->GetEntityType() == SMDSEntity_Triangle)) {
706
707     //  1 +--+ A  theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
708     //    | /|    theTria2: ( B A 2 ) B->1 ( 1 A 2 )   |\ |
709     //    |/ |                                         | \|
710     //  B +--+ 2                                     B +--+ 2
711
712     // put nodes in array and find out indices of the same ones
713     const SMDS_MeshNode* aNodes [6];
714     int sameInd [] = { -1, -1, -1, -1, -1, -1 };
715     int i = 0;
716     SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
717     while ( it->more() ) {
718       aNodes[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
719
720       if ( i > 2 ) // theTria2
721         // find same node of theTria1
722         for ( int j = 0; j < 3; j++ )
723           if ( aNodes[ i ] == aNodes[ j ]) {
724             sameInd[ j ] = i;
725             sameInd[ i ] = j;
726             break;
727           }
728       // next
729       i++;
730       if ( i == 3 ) {
731         if ( it->more() )
732           return false; // theTria1 is not a triangle
733         it = theTria2->nodesIterator();
734       }
735       if ( i == 6 && it->more() )
736         return false; // theTria2 is not a triangle
737     }
738
739     // find indices of 1,2 and of A,B in theTria1
740     int iA = -1, iB = 0, i1 = 0, i2 = 0;
741     for ( i = 0; i < 6; i++ ) {
742       if ( sameInd [ i ] == -1 ) {
743         if ( i < 3 ) i1 = i;
744         else         i2 = i;
745       }
746       else if (i < 3) {
747         if ( iA >= 0) iB = i;
748         else          iA = i;
749       }
750     }
751     // nodes 1 and 2 should not be the same
752     if ( aNodes[ i1 ] == aNodes[ i2 ] )
753       return false;
754
755     // theTria1: A->2
756     aNodes[ iA ] = aNodes[ i2 ];
757     // theTria2: B->1
758     aNodes[ sameInd[ iB ]] = aNodes[ i1 ];
759
760     GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 );
761     GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 );
762
763     return true;
764
765   } // end if(F1 && F2)
766
767   // check case of quadratic faces
768   if (theTria1->GetEntityType() != SMDSEntity_Quad_Triangle &&
769       theTria1->GetEntityType() != SMDSEntity_BiQuad_Triangle)
770     return false;
771   if (theTria2->GetEntityType() != SMDSEntity_Quad_Triangle&&
772       theTria2->GetEntityType() != SMDSEntity_BiQuad_Triangle)
773     return false;
774
775   //       5
776   //  1 +--+--+ 2  theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
777   //    |    /|    theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
778   //    |   / |
779   //  7 +  +  + 6
780   //    | /9  |
781   //    |/    |
782   //  4 +--+--+ 3
783   //       8
784
785   vector< const SMDS_MeshNode* > N1;
786   vector< const SMDS_MeshNode* > N2;
787   if(!getNodesFromTwoTria(theTria1,theTria2,N1,N2))
788     return false;
789   // now we receive following N1 and N2 (using numeration as above image)
790   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
791   // i.e. first nodes from both arrays determ new diagonal
792
793   vector< const SMDS_MeshNode*> N1new( N1.size() );
794   vector< const SMDS_MeshNode*> N2new( N2.size() );
795   N1new.back() = N1.back(); // central node of biquadratic
796   N2new.back() = N2.back();
797   N1new[0] = N1[0];  N2new[0] = N1[0];
798   N1new[1] = N2[0];  N2new[1] = N1[1];
799   N1new[2] = N2[1];  N2new[2] = N2[0];
800   N1new[3] = N1[4];  N2new[3] = N1[3];
801   N1new[4] = N2[3];  N2new[4] = N2[5];
802   N1new[5] = N1[5];  N2new[5] = N1[4];
803   // change nodes in faces
804   GetMeshDS()->ChangeElementNodes( theTria1, &N1new[0], N1new.size() );
805   GetMeshDS()->ChangeElementNodes( theTria2, &N2new[0], N2new.size() );
806
807   // move the central node of biquadratic triangle
808   SMESH_MesherHelper helper( *GetMesh() );
809   for ( int is2nd = 0; is2nd < 2; ++is2nd )
810   {
811     const SMDS_MeshElement*         tria = is2nd ? theTria2 : theTria1;
812     vector< const SMDS_MeshNode*>& nodes = is2nd ? N2new : N1new;
813     if ( nodes.size() < 7 )
814       continue;
815     helper.SetSubShape( tria->getshapeId() );
816     const TopoDS_Face& F = TopoDS::Face( helper.GetSubShape() );
817     gp_Pnt xyz;
818     if ( F.IsNull() )
819     {
820       xyz = ( SMESH_TNodeXYZ( nodes[3] ) +
821               SMESH_TNodeXYZ( nodes[4] ) +
822               SMESH_TNodeXYZ( nodes[5] )) / 3.;
823     }
824     else
825     {
826       bool checkUV;
827       gp_XY uv = ( helper.GetNodeUV( F, nodes[3], nodes[2], &checkUV ) +
828                    helper.GetNodeUV( F, nodes[4], nodes[0], &checkUV ) +
829                    helper.GetNodeUV( F, nodes[5], nodes[1], &checkUV )) / 3.;
830       TopLoc_Location loc;
831       Handle(Geom_Surface) S = BRep_Tool::Surface(F,loc);
832       xyz = S->Value( uv.X(), uv.Y() );
833       xyz.Transform( loc );
834       if ( nodes[6]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE &&  // set UV
835            nodes[6]->getshapeId() > 0 )
836         GetMeshDS()->SetNodeOnFace( nodes[6], nodes[6]->getshapeId(), uv.X(), uv.Y() );
837     }
838     GetMeshDS()->MoveNode( nodes[6], xyz.X(), xyz.Y(), xyz.Z() );
839   }
840   return true;
841 }
842
843 //=======================================================================
844 //function : findTriangles
845 //purpose  : find triangles sharing theNode1-theNode2 link
846 //=======================================================================
847
848 static bool findTriangles(const SMDS_MeshNode *    theNode1,
849                           const SMDS_MeshNode *    theNode2,
850                           const SMDS_MeshElement*& theTria1,
851                           const SMDS_MeshElement*& theTria2)
852 {
853   if ( !theNode1 || !theNode2 ) return false;
854
855   theTria1 = theTria2 = 0;
856
857   set< const SMDS_MeshElement* > emap;
858   SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face);
859   while (it->more()) {
860     const SMDS_MeshElement* elem = it->next();
861     if ( elem->NbCornerNodes() == 3 )
862       emap.insert( elem );
863   }
864   it = theNode2->GetInverseElementIterator(SMDSAbs_Face);
865   while (it->more()) {
866     const SMDS_MeshElement* elem = it->next();
867     if ( emap.count( elem )) {
868       if ( !theTria1 )
869       {
870         theTria1 = elem;
871       }
872       else  
873       {
874         theTria2 = elem;
875         // theTria1 must be element with minimum ID
876         if ( theTria2->GetID() < theTria1->GetID() )
877           std::swap( theTria2, theTria1 );
878         return true;
879       }
880     }
881   }
882   return false;
883 }
884
885 //=======================================================================
886 //function : InverseDiag
887 //purpose  : Replace two neighbour triangles sharing theNode1-theNode2 link
888 //           with ones built on the same 4 nodes but having other common link.
889 //           Return false if proper faces not found
890 //=======================================================================
891
892 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
893                                     const SMDS_MeshNode * theNode2)
894 {
895   myLastCreatedElems.Clear();
896   myLastCreatedNodes.Clear();
897
898   const SMDS_MeshElement *tr1, *tr2;
899   if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
900     return false;
901
902   const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
903   if (!F1) return false;
904   const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
905   if (!F2) return false;
906   if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
907       (tr2->GetEntityType() == SMDSEntity_Triangle)) {
908
909     //  1 +--+ A  tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
910     //    | /|    tr2: ( B A 2 ) B->1 ( 1 A 2 )   |\ |
911     //    |/ |                                    | \|
912     //  B +--+ 2                                B +--+ 2
913
914     // put nodes in array
915     // and find indices of 1,2 and of A in tr1 and of B in tr2
916     int i, iA1 = 0, i1 = 0;
917     const SMDS_MeshNode* aNodes1 [3];
918     SMDS_ElemIteratorPtr it;
919     for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) {
920       aNodes1[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
921       if ( aNodes1[ i ] == theNode1 )
922         iA1 = i; // node A in tr1
923       else if ( aNodes1[ i ] != theNode2 )
924         i1 = i;  // node 1
925     }
926     int iB2 = 0, i2 = 0;
927     const SMDS_MeshNode* aNodes2 [3];
928     for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) {
929       aNodes2[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
930       if ( aNodes2[ i ] == theNode2 )
931         iB2 = i; // node B in tr2
932       else if ( aNodes2[ i ] != theNode1 )
933         i2 = i;  // node 2
934     }
935
936     // nodes 1 and 2 should not be the same
937     if ( aNodes1[ i1 ] == aNodes2[ i2 ] )
938       return false;
939
940     // tr1: A->2
941     aNodes1[ iA1 ] = aNodes2[ i2 ];
942     // tr2: B->1
943     aNodes2[ iB2 ] = aNodes1[ i1 ];
944
945     GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 );
946     GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 );
947
948     return true;
949   }
950
951   // check case of quadratic faces
952   return InverseDiag(tr1,tr2);
953 }
954
955 //=======================================================================
956 //function : getQuadrangleNodes
957 //purpose  : fill theQuadNodes - nodes of a quadrangle resulting from
958 //           fusion of triangles tr1 and tr2 having shared link on
959 //           theNode1 and theNode2
960 //=======================================================================
961
962 bool getQuadrangleNodes(const SMDS_MeshNode *    theQuadNodes [],
963                         const SMDS_MeshNode *    theNode1,
964                         const SMDS_MeshNode *    theNode2,
965                         const SMDS_MeshElement * tr1,
966                         const SMDS_MeshElement * tr2 )
967 {
968   if( tr1->NbNodes() != tr2->NbNodes() )
969     return false;
970   // find the 4-th node to insert into tr1
971   const SMDS_MeshNode* n4 = 0;
972   SMDS_ElemIteratorPtr it = tr2->nodesIterator();
973   int i=0;
974   while ( !n4 && i<3 ) {
975     const SMDS_MeshNode * n = cast2Node( it->next() );
976     i++;
977     bool isDiag = ( n == theNode1 || n == theNode2 );
978     if ( !isDiag )
979       n4 = n;
980   }
981   // Make an array of nodes to be in a quadrangle
982   int iNode = 0, iFirstDiag = -1;
983   it = tr1->nodesIterator();
984   i=0;
985   while ( i<3 ) {
986     const SMDS_MeshNode * n = cast2Node( it->next() );
987     i++;
988     bool isDiag = ( n == theNode1 || n == theNode2 );
989     if ( isDiag ) {
990       if ( iFirstDiag < 0 )
991         iFirstDiag = iNode;
992       else if ( iNode - iFirstDiag == 1 )
993         theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes
994     }
995     else if ( n == n4 ) {
996       return false; // tr1 and tr2 should not have all the same nodes
997     }
998     theQuadNodes[ iNode++ ] = n;
999   }
1000   if ( iNode == 3 ) // diagonal nodes have 0 and 2 indices
1001     theQuadNodes[ iNode ] = n4;
1002
1003   return true;
1004 }
1005
1006 //=======================================================================
1007 //function : DeleteDiag
1008 //purpose  : Replace two neighbour triangles sharing theNode1-theNode2 link
1009 //           with a quadrangle built on the same 4 nodes.
1010 //           Return false if proper faces not found
1011 //=======================================================================
1012
1013 bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
1014                                    const SMDS_MeshNode * theNode2)
1015 {
1016   myLastCreatedElems.Clear();
1017   myLastCreatedNodes.Clear();
1018
1019   const SMDS_MeshElement *tr1, *tr2;
1020   if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
1021     return false;
1022
1023   const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
1024   if (!F1) return false;
1025   const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
1026   if (!F2) return false;
1027   SMESHDS_Mesh * aMesh = GetMeshDS();
1028
1029   if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
1030       (tr2->GetEntityType() == SMDSEntity_Triangle)) {
1031
1032     const SMDS_MeshNode* aNodes [ 4 ];
1033     if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 ))
1034       return false;
1035
1036     const SMDS_MeshElement* newElem = 0;
1037     newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3] );
1038     myLastCreatedElems.Append(newElem);
1039     AddToSameGroups( newElem, tr1, aMesh );
1040     int aShapeId = tr1->getshapeId();
1041     if ( aShapeId )
1042       {
1043         aMesh->SetMeshElementOnShape( newElem, aShapeId );
1044       }
1045     aMesh->RemoveElement( tr1 );
1046     aMesh->RemoveElement( tr2 );
1047
1048     return true;
1049   }
1050
1051   // check case of quadratic faces
1052   if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
1053     return false;
1054   if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
1055     return false;
1056
1057   //       5
1058   //  1 +--+--+ 2  tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
1059   //    |    /|    tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
1060   //    |   / |
1061   //  7 +  +  + 6
1062   //    | /9  |
1063   //    |/    |
1064   //  4 +--+--+ 3
1065   //       8
1066
1067   vector< const SMDS_MeshNode* > N1;
1068   vector< const SMDS_MeshNode* > N2;
1069   if(!getNodesFromTwoTria(tr1,tr2,N1,N2))
1070     return false;
1071   // now we receive following N1 and N2 (using numeration as above image)
1072   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
1073   // i.e. first nodes from both arrays determ new diagonal
1074
1075   const SMDS_MeshNode* aNodes[8];
1076   aNodes[0] = N1[0];
1077   aNodes[1] = N1[1];
1078   aNodes[2] = N2[0];
1079   aNodes[3] = N2[1];
1080   aNodes[4] = N1[3];
1081   aNodes[5] = N2[5];
1082   aNodes[6] = N2[3];
1083   aNodes[7] = N1[5];
1084
1085   const SMDS_MeshElement* newElem = 0;
1086   newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
1087                             aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
1088   myLastCreatedElems.Append(newElem);
1089   AddToSameGroups( newElem, tr1, aMesh );
1090   int aShapeId = tr1->getshapeId();
1091   if ( aShapeId )
1092     {
1093       aMesh->SetMeshElementOnShape( newElem, aShapeId );
1094     }
1095   aMesh->RemoveElement( tr1 );
1096   aMesh->RemoveElement( tr2 );
1097
1098   // remove middle node (9)
1099   GetMeshDS()->RemoveNode( N1[4] );
1100
1101   return true;
1102 }
1103
1104 //=======================================================================
1105 //function : Reorient
1106 //purpose  : Reverse theElement orientation
1107 //=======================================================================
1108
1109 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
1110 {
1111   myLastCreatedElems.Clear();
1112   myLastCreatedNodes.Clear();
1113
1114   if (!theElem)
1115     return false;
1116   SMDS_ElemIteratorPtr it = theElem->nodesIterator();
1117   if ( !it || !it->more() )
1118     return false;
1119
1120   const SMDSAbs_ElementType type = theElem->GetType();
1121   if ( type < SMDSAbs_Edge || type > SMDSAbs_Volume )
1122     return false;
1123
1124   const SMDSAbs_EntityType geomType = theElem->GetEntityType();
1125   if ( geomType == SMDSEntity_Polyhedra ) // polyhedron
1126   {
1127     const SMDS_VtkVolume* aPolyedre =
1128       dynamic_cast<const SMDS_VtkVolume*>( theElem );
1129     if (!aPolyedre) {
1130       MESSAGE("Warning: bad volumic element");
1131       return false;
1132     }
1133     const int nbFaces = aPolyedre->NbFaces();
1134     vector<const SMDS_MeshNode *> poly_nodes;
1135     vector<int> quantities (nbFaces);
1136
1137     // reverse each face of the polyedre
1138     for (int iface = 1; iface <= nbFaces; iface++) {
1139       int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
1140       quantities[iface - 1] = nbFaceNodes;
1141
1142       for (inode = nbFaceNodes; inode >= 1; inode--) {
1143         const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
1144         poly_nodes.push_back(curNode);
1145       }
1146     }
1147     return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
1148   }
1149   else // other elements
1150   {
1151     vector<const SMDS_MeshNode*> nodes( theElem->begin_nodes(), theElem->end_nodes() );
1152     const std::vector<int>& interlace = SMDS_MeshCell::reverseSmdsOrder( geomType, nodes.size() );
1153     if ( interlace.empty() )
1154     {
1155       std::reverse( nodes.begin(), nodes.end() ); // obsolete, just in case
1156     }
1157     else
1158     {
1159       SMDS_MeshCell::applyInterlace( interlace, nodes );
1160     }
1161     return GetMeshDS()->ChangeElementNodes( theElem, &nodes[0], nodes.size() );
1162   }
1163   return false;
1164 }
1165
1166 //================================================================================
1167 /*!
1168  * \brief Reorient faces.
1169  * \param theFaces - the faces to reorient. If empty the whole mesh is meant
1170  * \param theDirection - desired direction of normal of \a theFace
1171  * \param theFace - one of \a theFaces that sould be oriented according to
1172  *        \a theDirection and whose orientation defines orientation of other faces
1173  * \return number of reoriented faces.
1174  */
1175 //================================================================================
1176
1177 int SMESH_MeshEditor::Reorient2D (TIDSortedElemSet &       theFaces,
1178                                   const gp_Dir&            theDirection,
1179                                   const SMDS_MeshElement * theFace)
1180 {
1181   int nbReori = 0;
1182   if ( !theFace || theFace->GetType() != SMDSAbs_Face ) return nbReori;
1183
1184   if ( theFaces.empty() )
1185   {
1186     SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator(/*idInceasingOrder=*/true);
1187     while ( fIt->more() )
1188       theFaces.insert( theFaces.end(), fIt->next() );
1189   }
1190
1191   // orient theFace according to theDirection
1192   gp_XYZ normal;
1193   SMESH_MeshAlgos::FaceNormal( theFace, normal, /*normalized=*/false );
1194   if ( normal * theDirection.XYZ() < 0 )
1195     nbReori += Reorient( theFace );
1196
1197   // Orient other faces
1198
1199   set< const SMDS_MeshElement* > startFaces, visitedFaces;
1200   TIDSortedElemSet avoidSet;
1201   set< SMESH_TLink > checkedLinks;
1202   pair< set< SMESH_TLink >::iterator, bool > linkIt_isNew;
1203
1204   if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces
1205     theFaces.erase( theFace );
1206   startFaces.insert( theFace );
1207
1208   int nodeInd1, nodeInd2;
1209   const SMDS_MeshElement*           otherFace;
1210   vector< const SMDS_MeshElement* > facesNearLink;
1211   vector< std::pair< int, int > >   nodeIndsOfFace;
1212
1213   set< const SMDS_MeshElement* >::iterator startFace = startFaces.begin();
1214   while ( !startFaces.empty() )
1215   {
1216     startFace = startFaces.begin();
1217     theFace = *startFace;
1218     startFaces.erase( startFace );
1219     if ( !visitedFaces.insert( theFace ).second )
1220       continue;
1221
1222     avoidSet.clear();
1223     avoidSet.insert(theFace);
1224
1225     NLink link( theFace->GetNode( 0 ), (SMDS_MeshNode *) 0 );
1226
1227     const int nbNodes = theFace->NbCornerNodes();
1228     for ( int i = 0; i < nbNodes; ++i ) // loop on links of theFace
1229     {
1230       link.second = theFace->GetNode(( i+1 ) % nbNodes );
1231       linkIt_isNew = checkedLinks.insert( link );
1232       if ( !linkIt_isNew.second )
1233       {
1234         // link has already been checked and won't be encountered more
1235         // if the group (theFaces) is manifold
1236         //checkedLinks.erase( linkIt_isNew.first );
1237       }
1238       else
1239       {
1240         facesNearLink.clear();
1241         nodeIndsOfFace.clear();
1242         while (( otherFace = SMESH_MeshAlgos::FindFaceInSet( link.first, link.second,
1243                                                              theFaces, avoidSet,
1244                                                              &nodeInd1, &nodeInd2 )))
1245           if ( otherFace != theFace)
1246           {
1247             facesNearLink.push_back( otherFace );
1248             nodeIndsOfFace.push_back( make_pair( nodeInd1, nodeInd2 ));
1249             avoidSet.insert( otherFace );
1250           }
1251         if ( facesNearLink.size() > 1 )
1252         {
1253           // NON-MANIFOLD mesh shell !
1254           // select a face most co-directed with theFace,
1255           // other faces won't be visited this time
1256           gp_XYZ NF, NOF;
1257           SMESH_MeshAlgos::FaceNormal( theFace, NF, /*normalized=*/false );
1258           double proj, maxProj = -1;
1259           for ( size_t i = 0; i < facesNearLink.size(); ++i ) {
1260             SMESH_MeshAlgos::FaceNormal( facesNearLink[i], NOF, /*normalized=*/false );
1261             if (( proj = Abs( NF * NOF )) > maxProj ) {
1262               maxProj = proj;
1263               otherFace = facesNearLink[i];
1264               nodeInd1  = nodeIndsOfFace[i].first;
1265               nodeInd2  = nodeIndsOfFace[i].second;
1266             }
1267           }
1268           // not to visit rejected faces
1269           for ( size_t i = 0; i < facesNearLink.size(); ++i )
1270             if ( facesNearLink[i] != otherFace && theFaces.size() > 1 )
1271               visitedFaces.insert( facesNearLink[i] );
1272         }
1273         else if ( facesNearLink.size() == 1 )
1274         {
1275           otherFace = facesNearLink[0];
1276           nodeInd1  = nodeIndsOfFace.back().first;
1277           nodeInd2  = nodeIndsOfFace.back().second;
1278         }
1279         if ( otherFace && otherFace != theFace)
1280         {
1281           // link must be reverse in otherFace if orientation ot otherFace
1282           // is same as that of theFace
1283           if ( abs(nodeInd2-nodeInd1) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1284           {
1285             nbReori += Reorient( otherFace );
1286           }
1287           startFaces.insert( otherFace );
1288         }
1289       }
1290       std::swap( link.first, link.second ); // reverse the link
1291     }
1292   }
1293   return nbReori;
1294 }
1295
1296 //================================================================================
1297 /*!
1298  * \brief Reorient faces basing on orientation of adjacent volumes.
1299  * \param theFaces - faces to reorient. If empty, all mesh faces are treated.
1300  * \param theVolumes - reference volumes.
1301  * \param theOutsideNormal - to orient faces to have their normal
1302  *        pointing either \a outside or \a inside the adjacent volumes.
1303  * \return number of reoriented faces.
1304  */
1305 //================================================================================
1306
1307 int SMESH_MeshEditor::Reorient2DBy3D (TIDSortedElemSet & theFaces,
1308                                       TIDSortedElemSet & theVolumes,
1309                                       const bool         theOutsideNormal)
1310 {
1311   int nbReori = 0;
1312
1313   SMDS_ElemIteratorPtr faceIt;
1314   if ( theFaces.empty() )
1315     faceIt = GetMeshDS()->elementsIterator( SMDSAbs_Face );
1316   else
1317     faceIt = elemSetIterator( theFaces );
1318
1319   vector< const SMDS_MeshNode* > faceNodes;
1320   TIDSortedElemSet checkedVolumes;
1321   set< const SMDS_MeshNode* > faceNodesSet;
1322   SMDS_VolumeTool volumeTool;
1323
1324   while ( faceIt->more() ) // loop on given faces
1325   {
1326     const SMDS_MeshElement* face = faceIt->next();
1327     if ( face->GetType() != SMDSAbs_Face )
1328       continue;
1329
1330     const size_t nbCornersNodes = face->NbCornerNodes();
1331     faceNodes.assign( face->begin_nodes(), face->end_nodes() );
1332
1333     checkedVolumes.clear();
1334     SMDS_ElemIteratorPtr vIt = faceNodes[ 0 ]->GetInverseElementIterator( SMDSAbs_Volume );
1335     while ( vIt->more() )
1336     {
1337       const SMDS_MeshElement* volume = vIt->next();
1338
1339       if ( !checkedVolumes.insert( volume ).second )
1340         continue;
1341       if ( !theVolumes.empty() && !theVolumes.count( volume ))
1342         continue;
1343
1344       // is volume adjacent?
1345       bool allNodesCommon = true;
1346       for ( size_t iN = 1; iN < nbCornersNodes && allNodesCommon; ++iN )
1347         allNodesCommon = ( volume->GetNodeIndex( faceNodes[ iN ]) > -1 );
1348       if ( !allNodesCommon )
1349         continue;
1350
1351       // get nodes of a corresponding volume facet
1352       faceNodesSet.clear();
1353       faceNodesSet.insert( faceNodes.begin(), faceNodes.end() );
1354       volumeTool.Set( volume );
1355       int facetID = volumeTool.GetFaceIndex( faceNodesSet );
1356       if ( facetID < 0 ) continue;
1357       volumeTool.SetExternalNormal();
1358       const SMDS_MeshNode** facetNodes = volumeTool.GetFaceNodes( facetID );
1359
1360       // compare order of faceNodes and facetNodes
1361       const int iQ = 1 + ( nbCornersNodes < faceNodes.size() );
1362       int iNN[2];
1363       for ( int i = 0; i < 2; ++i )
1364       {
1365         const SMDS_MeshNode* n = facetNodes[ i*iQ ];
1366         for ( size_t iN = 0; iN < nbCornersNodes; ++iN )
1367           if ( faceNodes[ iN ] == n )
1368           {
1369             iNN[ i ] = iN;
1370             break;
1371           }
1372       }
1373       bool isOutside = Abs( iNN[0]-iNN[1] ) == 1 ? iNN[0] < iNN[1] : iNN[0] > iNN[1];
1374       if ( isOutside != theOutsideNormal )
1375         nbReori += Reorient( face );
1376     }
1377   }  // loop on given faces
1378
1379   return nbReori;
1380 }
1381
1382 //=======================================================================
1383 //function : getBadRate
1384 //purpose  :
1385 //=======================================================================
1386
1387 static double getBadRate (const SMDS_MeshElement*               theElem,
1388                           SMESH::Controls::NumericalFunctorPtr& theCrit)
1389 {
1390   SMESH::Controls::TSequenceOfXYZ P;
1391   if ( !theElem || !theCrit->GetPoints( theElem, P ))
1392     return 1e100;
1393   return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1394   //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1395 }
1396
1397 //=======================================================================
1398 //function : QuadToTri
1399 //purpose  : Cut quadrangles into triangles.
1400 //           theCrit is used to select a diagonal to cut
1401 //=======================================================================
1402
1403 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet &                   theElems,
1404                                   SMESH::Controls::NumericalFunctorPtr theCrit)
1405 {
1406   myLastCreatedElems.Clear();
1407   myLastCreatedNodes.Clear();
1408
1409   if ( !theCrit.get() )
1410     return false;
1411
1412   SMESHDS_Mesh * aMesh = GetMeshDS();
1413
1414   Handle(Geom_Surface) surface;
1415   SMESH_MesherHelper   helper( *GetMesh() );
1416
1417   TIDSortedElemSet::iterator itElem;
1418   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
1419   {
1420     const SMDS_MeshElement* elem = *itElem;
1421     if ( !elem || elem->GetType() != SMDSAbs_Face )
1422       continue;
1423     if ( elem->NbCornerNodes() != 4 )
1424       continue;
1425
1426     // retrieve element nodes
1427     vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1428
1429     // compare two sets of possible triangles
1430     double aBadRate1, aBadRate2; // to what extent a set is bad
1431     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1432     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1433     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1434
1435     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1436     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1437     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1438
1439     const int aShapeId = FindShape( elem );
1440     const SMDS_MeshElement* newElem1 = 0;
1441     const SMDS_MeshElement* newElem2 = 0;
1442
1443     if ( !elem->IsQuadratic() ) // split liner quadrangle
1444     {
1445       // for MaxElementLength2D functor we return minimum diagonal for splitting,
1446       // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1447       if ( aBadRate1 <= aBadRate2 ) {
1448         // tr1 + tr2 is better
1449         newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1450         newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1451       }
1452       else {
1453         // tr3 + tr4 is better
1454         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1455         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1456       }
1457     }
1458     else // split quadratic quadrangle
1459     {
1460       helper.SetIsQuadratic( true );
1461       helper.SetIsBiQuadratic( aNodes.size() == 9 );
1462
1463       helper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem ));
1464       if ( aNodes.size() == 9 )
1465       {
1466         helper.SetIsBiQuadratic( true );
1467         if ( aBadRate1 <= aBadRate2 )
1468           helper.AddTLinkNode( aNodes[0], aNodes[2], aNodes[8] );
1469         else
1470           helper.AddTLinkNode( aNodes[1], aNodes[3], aNodes[8] );
1471       }
1472       // create a new element
1473       if ( aBadRate1 <= aBadRate2 ) {
1474         newElem1 = helper.AddFace( aNodes[2], aNodes[3], aNodes[0] );
1475         newElem2 = helper.AddFace( aNodes[2], aNodes[0], aNodes[1] );
1476       }
1477       else {
1478         newElem1 = helper.AddFace( aNodes[3], aNodes[0], aNodes[1] );
1479         newElem2 = helper.AddFace( aNodes[3], aNodes[1], aNodes[2] );
1480       }
1481     } // quadratic case
1482
1483     // care of a new element
1484
1485     myLastCreatedElems.Append(newElem1);
1486     myLastCreatedElems.Append(newElem2);
1487     AddToSameGroups( newElem1, elem, aMesh );
1488     AddToSameGroups( newElem2, elem, aMesh );
1489
1490     // put a new triangle on the same shape
1491     if ( aShapeId )
1492       aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1493     aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1494
1495     aMesh->RemoveElement( elem );
1496   }
1497   return true;
1498 }
1499
1500 //=======================================================================
1501 /*!
1502  * \brief Split each of given quadrangles into 4 triangles.
1503  * \param theElems - The faces to be splitted. If empty all faces are split.
1504  */
1505 //=======================================================================
1506
1507 void SMESH_MeshEditor::QuadTo4Tri (TIDSortedElemSet & theElems)
1508 {
1509   myLastCreatedElems.Clear();
1510   myLastCreatedNodes.Clear();
1511
1512   SMESH_MesherHelper helper( *GetMesh() );
1513   helper.SetElementsOnShape( true );
1514
1515   SMDS_ElemIteratorPtr faceIt;
1516   if ( theElems.empty() ) faceIt = GetMeshDS()->elementsIterator(SMDSAbs_Face);
1517   else                    faceIt = elemSetIterator( theElems );
1518
1519   bool   checkUV;
1520   gp_XY  uv [9]; uv[8] = gp_XY(0,0);
1521   gp_XYZ xyz[9];
1522   vector< const SMDS_MeshNode* > nodes;
1523   SMESHDS_SubMesh*               subMeshDS = 0;
1524   TopoDS_Face                    F;
1525   Handle(Geom_Surface)           surface;
1526   TopLoc_Location                loc;
1527
1528   while ( faceIt->more() )
1529   {
1530     const SMDS_MeshElement* quad = faceIt->next();
1531     if ( !quad || quad->NbCornerNodes() != 4 )
1532       continue;
1533
1534     // get a surface the quad is on
1535
1536     if ( quad->getshapeId() < 1 )
1537     {
1538       F.Nullify();
1539       helper.SetSubShape( 0 );
1540       subMeshDS = 0;
1541     }
1542     else if ( quad->getshapeId() != helper.GetSubShapeID() )
1543     {
1544       helper.SetSubShape( quad->getshapeId() );
1545       if ( !helper.GetSubShape().IsNull() &&
1546            helper.GetSubShape().ShapeType() == TopAbs_FACE )
1547       {
1548         F = TopoDS::Face( helper.GetSubShape() );
1549         surface = BRep_Tool::Surface( F, loc );
1550         subMeshDS = GetMeshDS()->MeshElements( quad->getshapeId() );
1551       }
1552       else
1553       {
1554         helper.SetSubShape( 0 );
1555         subMeshDS = 0;
1556       }
1557     }
1558
1559     // create a central node
1560
1561     const SMDS_MeshNode* nCentral;
1562     nodes.assign( quad->begin_nodes(), quad->end_nodes() );
1563
1564     if ( nodes.size() == 9 )
1565     {
1566       nCentral = nodes.back();
1567     }
1568     else
1569     {
1570       size_t iN = 0;
1571       if ( F.IsNull() )
1572       {
1573         for ( ; iN < nodes.size(); ++iN )
1574           xyz[ iN ] = SMESH_TNodeXYZ( nodes[ iN ] );
1575
1576         for ( ; iN < 8; ++iN ) // mid-side points of a linear qudrangle
1577           xyz[ iN ] = 0.5 * ( xyz[ iN - 4 ] + xyz[( iN - 3 )%4 ] );
1578
1579         xyz[ 8 ] = helper.calcTFI( 0.5, 0.5,
1580                                    xyz[0], xyz[1], xyz[2], xyz[3],
1581                                    xyz[4], xyz[5], xyz[6], xyz[7] );
1582       }
1583       else
1584       {
1585         for ( ; iN < nodes.size(); ++iN )
1586           uv[ iN ] = helper.GetNodeUV( F, nodes[iN], nodes[(iN+2)%4], &checkUV );
1587
1588         for ( ; iN < 8; ++iN ) // UV of mid-side points of a linear qudrangle
1589           uv[ iN ] = helper.GetMiddleUV( surface, uv[ iN - 4 ], uv[( iN - 3 )%4 ] );
1590
1591         uv[ 8 ] = helper.calcTFI( 0.5, 0.5,
1592                                   uv[0], uv[1], uv[2], uv[3],
1593                                   uv[4], uv[5], uv[6], uv[7] );
1594
1595         gp_Pnt p = surface->Value( uv[8].X(), uv[8].Y() ).Transformed( loc );
1596         xyz[ 8 ] = p.XYZ();
1597       }
1598
1599       nCentral = helper.AddNode( xyz[8].X(), xyz[8].Y(), xyz[8].Z(), /*id=*/0,
1600                                  uv[8].X(), uv[8].Y() );
1601       myLastCreatedNodes.Append( nCentral );
1602     }
1603
1604     // create 4 triangles
1605
1606     helper.SetIsQuadratic  ( nodes.size() > 4 );
1607     helper.SetIsBiQuadratic( nodes.size() == 9 );
1608     if ( helper.GetIsQuadratic() )
1609       helper.AddTLinks( static_cast< const SMDS_MeshFace*>( quad ));
1610
1611     GetMeshDS()->RemoveFreeElement( quad, subMeshDS, /*fromGroups=*/false );
1612
1613     for ( int i = 0; i < 4; ++i )
1614     {
1615       SMDS_MeshElement* tria = helper.AddFace( nodes[ i ],
1616                                                nodes[(i+1)%4],
1617                                                nCentral );
1618       ReplaceElemInGroups( tria, quad, GetMeshDS() );
1619       myLastCreatedElems.Append( tria );
1620     }
1621   }
1622 }
1623
1624 //=======================================================================
1625 //function : BestSplit
1626 //purpose  : Find better diagonal for cutting.
1627 //=======================================================================
1628
1629 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement*              theQuad,
1630                                  SMESH::Controls::NumericalFunctorPtr theCrit)
1631 {
1632   myLastCreatedElems.Clear();
1633   myLastCreatedNodes.Clear();
1634
1635   if (!theCrit.get())
1636     return -1;
1637
1638   if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1639     return -1;
1640
1641   if( theQuad->NbNodes()==4 ||
1642       (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1643
1644     // retrieve element nodes
1645     const SMDS_MeshNode* aNodes [4];
1646     SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1647     int i = 0;
1648     //while (itN->more())
1649     while (i<4) {
1650       aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1651     }
1652     // compare two sets of possible triangles
1653     double aBadRate1, aBadRate2; // to what extent a set is bad
1654     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1655     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1656     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1657
1658     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1659     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1660     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1661     // for MaxElementLength2D functor we return minimum diagonal for splitting,
1662     // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1663     if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1664       return 1; // diagonal 1-3
1665
1666     return 2; // diagonal 2-4
1667   }
1668   return -1;
1669 }
1670
1671 namespace
1672 {
1673   // Methods of splitting volumes into tetra
1674
1675   const int theHexTo5_1[5*4+1] =
1676     {
1677       0, 1, 2, 5,    0, 4, 5, 7,     0, 2, 3, 7,    2, 5, 6, 7,     0, 5, 2, 7,   -1
1678     };
1679   const int theHexTo5_2[5*4+1] =
1680     {
1681       1, 2, 3, 6,    1, 4, 5, 6,     0, 1, 3, 4,    3, 4, 6, 7,     1, 3, 4, 6,   -1
1682     };
1683   const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1684
1685   const int theHexTo6_1[6*4+1] =
1686     {
1687       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
1688     };
1689   const int theHexTo6_2[6*4+1] =
1690     {
1691       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
1692     };
1693   const int theHexTo6_3[6*4+1] =
1694     {
1695       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
1696     };
1697   const int theHexTo6_4[6*4+1] =
1698     {
1699       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
1700     };
1701   const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1702
1703   const int thePyraTo2_1[2*4+1] =
1704     {
1705       0, 1, 2, 4,    0, 2, 3, 4,   -1
1706     };
1707   const int thePyraTo2_2[2*4+1] =
1708     {
1709       1, 2, 3, 4,    1, 3, 0, 4,   -1
1710     };
1711   const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1712
1713   const int thePentaTo3_1[3*4+1] =
1714     {
1715       0, 1, 2, 3,    1, 3, 4, 2,     2, 3, 4, 5,    -1
1716     };
1717   const int thePentaTo3_2[3*4+1] =
1718     {
1719       1, 2, 0, 4,    2, 4, 5, 0,     0, 4, 5, 3,    -1
1720     };
1721   const int thePentaTo3_3[3*4+1] =
1722     {
1723       2, 0, 1, 5,    0, 5, 3, 1,     1, 5, 3, 4,    -1
1724     };
1725   const int thePentaTo3_4[3*4+1] =
1726     {
1727       0, 1, 2, 3,    1, 3, 4, 5,     2, 3, 1, 5,    -1
1728     };
1729   const int thePentaTo3_5[3*4+1] =
1730     {
1731       1, 2, 0, 4,    2, 4, 5, 3,     0, 4, 2, 3,    -1
1732     };
1733   const int thePentaTo3_6[3*4+1] =
1734     {
1735       2, 0, 1, 5,    0, 5, 3, 4,     1, 5, 0, 4,    -1
1736     };
1737   const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1738                                 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1739
1740   // Methods of splitting hexahedron into prisms
1741
1742   const int theHexTo4Prisms_BT[6*4+1] = // bottom-top
1743     {
1744       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
1745     };
1746   const int theHexTo4Prisms_LR[6*4+1] = // left-right
1747     {
1748       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
1749     };
1750   const int theHexTo4Prisms_FB[6*4+1] = // front-back
1751     {
1752       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
1753     };
1754
1755   const int theHexTo2Prisms_BT_1[6*2+1] =
1756     {
1757       0, 1, 3, 4, 5, 7,    1, 2, 3, 5, 6, 7,   -1
1758     };
1759   const int theHexTo2Prisms_BT_2[6*2+1] =
1760     {
1761       0, 1, 2, 4, 5, 6,    0, 2, 3, 4, 6, 7,   -1
1762     };
1763   const int* theHexTo2Prisms_BT[2] = { theHexTo2Prisms_BT_1, theHexTo2Prisms_BT_2 };
1764
1765   const int theHexTo2Prisms_LR_1[6*2+1] =
1766     {
1767       1, 0, 4, 2, 3, 7,    1, 4, 5, 2, 7, 6,   -1
1768     };
1769   const int theHexTo2Prisms_LR_2[6*2+1] =
1770     {
1771       1, 0, 4, 2, 3, 7,    1, 4, 5, 2, 7, 6,   -1
1772     };
1773   const int* theHexTo2Prisms_LR[2] = { theHexTo2Prisms_LR_1, theHexTo2Prisms_LR_2 };
1774
1775   const int theHexTo2Prisms_FB_1[6*2+1] =
1776     {
1777       0, 3, 4, 1, 2, 5,    3, 7, 4, 2, 6, 5,   -1
1778     };
1779   const int theHexTo2Prisms_FB_2[6*2+1] =
1780     {
1781       0, 3, 7, 1, 2, 7,    0, 7, 4, 1, 6, 5,   -1
1782     };
1783   const int* theHexTo2Prisms_FB[2] = { theHexTo2Prisms_FB_1, theHexTo2Prisms_FB_2 };
1784
1785
1786   struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1787   {
1788     int _n1, _n2, _n3;
1789     TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1790     bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1791     bool hasAdjacentVol( const SMDS_MeshElement*    elem,
1792                          const SMDSAbs_GeometryType geom = SMDSGeom_TETRA) const;
1793   };
1794   struct TSplitMethod
1795   {
1796     int        _nbSplits;
1797     int        _nbCorners;
1798     const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1799     bool       _baryNode;     //!< additional node is to be created at cell barycenter
1800     bool       _ownConn;      //!< to delete _connectivity in destructor
1801     map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1802
1803     TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1804       : _nbSplits(nbTet), _nbCorners(4), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1805     ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1806     bool hasFacet( const TTriangleFacet& facet ) const
1807     {
1808       if ( _nbCorners == 4 )
1809       {
1810         const int* tetConn = _connectivity;
1811         for ( ; tetConn[0] >= 0; tetConn += 4 )
1812           if (( facet.contains( tetConn[0] ) +
1813                 facet.contains( tetConn[1] ) +
1814                 facet.contains( tetConn[2] ) +
1815                 facet.contains( tetConn[3] )) == 3 )
1816             return true;
1817       }
1818       else // prism, _nbCorners == 6
1819       {
1820         const int* prismConn = _connectivity;
1821         for ( ; prismConn[0] >= 0; prismConn += 6 )
1822         {
1823           if (( facet.contains( prismConn[0] ) &&
1824                 facet.contains( prismConn[1] ) &&
1825                 facet.contains( prismConn[2] ))
1826               ||
1827               ( facet.contains( prismConn[3] ) &&
1828                 facet.contains( prismConn[4] ) &&
1829                 facet.contains( prismConn[5] )))
1830             return true;
1831         }
1832       }
1833       return false;
1834     }
1835   };
1836
1837   //=======================================================================
1838   /*!
1839    * \brief return TSplitMethod for the given element to split into tetrahedra
1840    */
1841   //=======================================================================
1842
1843   TSplitMethod getTetraSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1844   {
1845     const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1846
1847     // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1848     // an edge and a face barycenter; tertaherdons are based on triangles and
1849     // a volume barycenter
1850     const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1851
1852     // Find out how adjacent volumes are split
1853
1854     vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1855     int hasAdjacentSplits = 0, maxTetConnSize = 0;
1856     for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1857     {
1858       int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1859       maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1860       if ( nbNodes < 4 ) continue;
1861
1862       list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1863       const int* nInd = vol.GetFaceNodesIndices( iF );
1864       if ( nbNodes == 4 )
1865       {
1866         TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1867         TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1868         if      ( t012.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t012 );
1869         else if ( t123.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t123 );
1870       }
1871       else
1872       {
1873         int iCom = 0; // common node of triangle faces to split into
1874         for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1875         {
1876           TTriangleFacet t012( nInd[ iQ * ( iCom             )],
1877                                nInd[ iQ * ( (iCom+1)%nbNodes )],
1878                                nInd[ iQ * ( (iCom+2)%nbNodes )]);
1879           TTriangleFacet t023( nInd[ iQ * ( iCom             )],
1880                                nInd[ iQ * ( (iCom+2)%nbNodes )],
1881                                nInd[ iQ * ( (iCom+3)%nbNodes )]);
1882           if ( t012.hasAdjacentVol( vol.Element() ) && t023.hasAdjacentVol( vol.Element() ))
1883           {
1884             triaSplits.push_back( t012 );
1885             triaSplits.push_back( t023 );
1886             break;
1887           }
1888         }
1889       }
1890       if ( !triaSplits.empty() )
1891         hasAdjacentSplits = true;
1892     }
1893
1894     // Among variants of split method select one compliant with adjacent volumes
1895
1896     TSplitMethod method;
1897     if ( !vol.Element()->IsPoly() && !is24TetMode )
1898     {
1899       int nbVariants = 2, nbTet = 0;
1900       const int** connVariants = 0;
1901       switch ( vol.Element()->GetEntityType() )
1902       {
1903       case SMDSEntity_Hexa:
1904       case SMDSEntity_Quad_Hexa:
1905       case SMDSEntity_TriQuad_Hexa:
1906         if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1907           connVariants = theHexTo5, nbTet = 5;
1908         else
1909           connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1910         break;
1911       case SMDSEntity_Pyramid:
1912       case SMDSEntity_Quad_Pyramid:
1913         connVariants = thePyraTo2;  nbTet = 2;
1914         break;
1915       case SMDSEntity_Penta:
1916       case SMDSEntity_Quad_Penta:
1917         connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1918         break;
1919       default:
1920         nbVariants = 0;
1921       }
1922       for ( int variant = 0; variant < nbVariants && method._nbSplits == 0; ++variant )
1923       {
1924         // check method compliancy with adjacent tetras,
1925         // all found splits must be among facets of tetras described by this method
1926         method = TSplitMethod( nbTet, connVariants[variant] );
1927         if ( hasAdjacentSplits && method._nbSplits > 0 )
1928         {
1929           bool facetCreated = true;
1930           for ( size_t iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1931           {
1932             list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1933             for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1934               facetCreated = method.hasFacet( *facet );
1935           }
1936           if ( !facetCreated )
1937             method = TSplitMethod(0); // incompatible method
1938         }
1939       }
1940     }
1941     if ( method._nbSplits < 1 )
1942     {
1943       // No standard method is applicable, use a generic solution:
1944       // each facet of a volume is split into triangles and
1945       // each of triangles and a volume barycenter form a tetrahedron.
1946
1947       const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1948
1949       int* connectivity = new int[ maxTetConnSize + 1 ];
1950       method._connectivity = connectivity;
1951       method._ownConn = true;
1952       method._baryNode = !isHex27; // to create central node or not
1953
1954       int connSize = 0;
1955       int baryCenInd = vol.NbNodes() - int( isHex27 );
1956       for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1957       {
1958         const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1959         const int*   nInd = vol.GetFaceNodesIndices( iF );
1960         // find common node of triangle facets of tetra to create
1961         int iCommon = 0; // index in linear numeration
1962         const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1963         if ( !triaSplits.empty() )
1964         {
1965           // by found facets
1966           const TTriangleFacet* facet = &triaSplits.front();
1967           for ( ; iCommon < nbNodes-1 ; ++iCommon )
1968             if ( facet->contains( nInd[ iQ * iCommon ]) &&
1969                  facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1970               break;
1971         }
1972         else if ( nbNodes > 3 && !is24TetMode )
1973         {
1974           // find the best method of splitting into triangles by aspect ratio
1975           SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1976           map< double, int > badness2iCommon;
1977           const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1978           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1979           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1980           {
1981             double badness = 0;
1982             for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1983             {
1984               SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon         )],
1985                                       nodes[ iQ*((iLast-1)%nbNodes)],
1986                                       nodes[ iQ*((iLast  )%nbNodes)]);
1987               badness += getBadRate( &tria, aspectRatio );
1988             }
1989             badness2iCommon.insert( make_pair( badness, iCommon ));
1990           }
1991           // use iCommon with lowest badness
1992           iCommon = badness2iCommon.begin()->second;
1993         }
1994         if ( iCommon >= nbNodes )
1995           iCommon = 0; // something wrong
1996
1997         // fill connectivity of tetrahedra based on a current face
1998         int nbTet = nbNodes - 2;
1999         if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
2000         {
2001           int faceBaryCenInd;
2002           if ( isHex27 )
2003           {
2004             faceBaryCenInd = vol.GetCenterNodeIndex( iF );
2005             method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
2006           }
2007           else
2008           {
2009             method._faceBaryNode[ iF ] = 0;
2010             faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
2011           }
2012           nbTet = nbNodes;
2013           for ( int i = 0; i < nbTet; ++i )
2014           {
2015             int i1 = i, i2 = (i+1) % nbNodes;
2016             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2017             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2018             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2019             connectivity[ connSize++ ] = faceBaryCenInd;
2020             connectivity[ connSize++ ] = baryCenInd;
2021           }
2022         }
2023         else
2024         {
2025           for ( int i = 0; i < nbTet; ++i )
2026           {
2027             int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
2028             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2029             connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
2030             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2031             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2032             connectivity[ connSize++ ] = baryCenInd;
2033           }
2034         }
2035         method._nbSplits += nbTet;
2036
2037       } // loop on volume faces
2038
2039       connectivity[ connSize++ ] = -1;
2040
2041     } // end of generic solution
2042
2043     return method;
2044   }
2045   //=======================================================================
2046   /*!
2047    * \brief return TSplitMethod to split haxhedron into prisms
2048    */
2049   //=======================================================================
2050
2051   TSplitMethod getPrismSplitMethod( SMDS_VolumeTool& vol,
2052                                     const int        methodFlags,
2053                                     const int        facetToSplit)
2054   {
2055     // order of facets in HEX according to SMDS_VolumeTool::Hexa_F :
2056     // B, T, L, B, R, F
2057     const int iF = ( facetToSplit < 2 ) ? 0 : 1 + ( facetToSplit-2 ) % 2; // [0,1,2]
2058
2059     if ( methodFlags == SMESH_MeshEditor::HEXA_TO_4_PRISMS )
2060     {
2061       static TSplitMethod to4methods[4]; // order BT, LR, FB
2062       if ( to4methods[iF]._nbSplits == 0 )
2063       {
2064         switch ( iF ) {
2065         case 0:
2066           to4methods[iF]._connectivity = theHexTo4Prisms_BT;
2067           to4methods[iF]._faceBaryNode[ 0 ] = 0;
2068           to4methods[iF]._faceBaryNode[ 1 ] = 0;
2069           break;
2070         case 1:
2071           to4methods[iF]._connectivity = theHexTo4Prisms_LR;
2072           to4methods[iF]._faceBaryNode[ 2 ] = 0;
2073           to4methods[iF]._faceBaryNode[ 4 ] = 0;
2074           break;
2075         case 2:
2076           to4methods[iF]._connectivity = theHexTo4Prisms_FB;
2077           to4methods[iF]._faceBaryNode[ 3 ] = 0;
2078           to4methods[iF]._faceBaryNode[ 5 ] = 0;
2079           break;
2080         default: return to4methods[3];
2081         }
2082         to4methods[iF]._nbSplits  = 4;
2083         to4methods[iF]._nbCorners = 6;
2084       }
2085       return to4methods[iF];
2086     }
2087     // else if ( methodFlags == HEXA_TO_2_PRISMS )
2088
2089     TSplitMethod method;
2090
2091     const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2092
2093     const int nbVariants = 2, nbSplits = 2;
2094     const int** connVariants = 0;
2095     switch ( iF ) {
2096     case 0: connVariants = theHexTo2Prisms_BT; break;
2097     case 1: connVariants = theHexTo2Prisms_LR; break;
2098     case 2: connVariants = theHexTo2Prisms_FB; break;
2099     default: return method;
2100     }
2101
2102     // look for prisms adjacent via facetToSplit and an opposite one
2103     for ( int is2nd = 0; is2nd < 2; ++is2nd )
2104     {
2105       int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2106       int nbNodes = vol.NbFaceNodes( iFacet ) / iQ;
2107       if ( nbNodes != 4 ) return method;
2108
2109       const int* nInd = vol.GetFaceNodesIndices( iFacet );
2110       TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2111       TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2112       TTriangleFacet* t;
2113       if      ( t012.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2114         t = &t012;
2115       else if ( t123.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2116         t = &t123;
2117       else
2118         continue;
2119
2120       // there are adjacent prism
2121       for ( int variant = 0; variant < nbVariants; ++variant )
2122       {
2123         // check method compliancy with adjacent prisms,
2124         // the found prism facets must be among facets of prisms described by current method
2125         method._nbSplits     = nbSplits;
2126         method._nbCorners    = 6;
2127         method._connectivity = connVariants[ variant ];
2128         if ( method.hasFacet( *t ))
2129           return method;
2130       }
2131     }
2132
2133     // No adjacent prisms. Select a variant with a best aspect ratio.
2134
2135     double badness[2] = { 0., 0. };
2136     static SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
2137     const SMDS_MeshNode** nodes = vol.GetNodes();
2138     for ( int variant = 0; variant < nbVariants; ++variant )
2139       for ( int is2nd = 0; is2nd < 2; ++is2nd )
2140       {
2141         int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2142         const int*             nInd = vol.GetFaceNodesIndices( iFacet );
2143
2144         method._connectivity = connVariants[ variant ];
2145         TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2146         TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2147         TTriangleFacet* t = ( method.hasFacet( t012 )) ? & t012 : & t123;
2148
2149         SMDS_FaceOfNodes tria ( nodes[ t->_n1 ],
2150                                 nodes[ t->_n2 ],
2151                                 nodes[ t->_n3 ] );
2152         badness[ variant ] += getBadRate( &tria, aspectRatio );
2153       }
2154     const int iBetter = ( badness[1] < badness[0] && badness[0]-badness[1] > 0.1 * badness[0] );
2155
2156     method._nbSplits     = nbSplits;
2157     method._nbCorners    = 6;
2158     method._connectivity = connVariants[ iBetter ];
2159
2160     return method;
2161   }
2162
2163   //================================================================================
2164   /*!
2165    * \brief Check if there is a tetraherdon adjacent to the given element via this facet
2166    */
2167   //================================================================================
2168
2169   bool TTriangleFacet::hasAdjacentVol( const SMDS_MeshElement*    elem,
2170                                        const SMDSAbs_GeometryType geom ) const
2171   {
2172     // find the tetrahedron including the three nodes of facet
2173     const SMDS_MeshNode* n1 = elem->GetNode(_n1);
2174     const SMDS_MeshNode* n2 = elem->GetNode(_n2);
2175     const SMDS_MeshNode* n3 = elem->GetNode(_n3);
2176     SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
2177     while ( volIt1->more() )
2178     {
2179       const SMDS_MeshElement* v = volIt1->next();
2180       if ( v->GetGeomType() != geom )
2181         continue;
2182       const int lastCornerInd = v->NbCornerNodes() - 1;
2183       if ( v->IsQuadratic() && v->GetNodeIndex( n1 ) > lastCornerInd )
2184         continue; // medium node not allowed
2185       const int ind2 = v->GetNodeIndex( n2 );
2186       if ( ind2 < 0 || lastCornerInd < ind2 )
2187         continue;
2188       const int ind3 = v->GetNodeIndex( n3 );
2189       if ( ind3 < 0 || lastCornerInd < ind3 )
2190         continue;
2191       return true;
2192     }
2193     return false;
2194   }
2195
2196   //=======================================================================
2197   /*!
2198    * \brief A key of a face of volume
2199    */
2200   //=======================================================================
2201
2202   struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
2203   {
2204     TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
2205     {
2206       TIDSortedNodeSet sortedNodes;
2207       const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2208       int nbNodes = vol.NbFaceNodes( iF );
2209       const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
2210       for ( int i = 0; i < nbNodes; i += iQ )
2211         sortedNodes.insert( fNodes[i] );
2212       TIDSortedNodeSet::iterator n = sortedNodes.begin();
2213       first.first   = (*(n++))->GetID();
2214       first.second  = (*(n++))->GetID();
2215       second.first  = (*(n++))->GetID();
2216       second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
2217     }
2218   };
2219 } // namespace
2220
2221 //=======================================================================
2222 //function : SplitVolumes
2223 //purpose  : Split volume elements into tetrahedra or prisms.
2224 //           If facet ID < 0, element is split into tetrahedra,
2225 //           else a hexahedron is split into prisms so that the given facet is
2226 //           split into triangles
2227 //=======================================================================
2228
2229 void SMESH_MeshEditor::SplitVolumes (const TFacetOfElem & theElems,
2230                                      const int            theMethodFlags)
2231 {
2232   SMDS_VolumeTool    volTool;
2233   SMESH_MesherHelper helper( *GetMesh()), fHelper(*GetMesh());
2234   fHelper.ToFixNodeParameters( true );
2235
2236   SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
2237   SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
2238
2239   SMESH_SequenceOfElemPtr newNodes, newElems;
2240
2241   // map face of volume to it's baricenrtic node
2242   map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
2243   double bc[3];
2244   vector<const SMDS_MeshElement* > splitVols;
2245
2246   TFacetOfElem::const_iterator elem2facet = theElems.begin();
2247   for ( ; elem2facet != theElems.end(); ++elem2facet )
2248   {
2249     const SMDS_MeshElement* elem = elem2facet->first;
2250     const int       facetToSplit = elem2facet->second;
2251     if ( elem->GetType() != SMDSAbs_Volume )
2252       continue;
2253     const SMDSAbs_EntityType geomType = elem->GetEntityType();
2254     if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
2255       continue;
2256
2257     if ( !volTool.Set( elem, /*ignoreCentralNodes=*/false )) continue; // strange...
2258
2259     TSplitMethod splitMethod = ( facetToSplit < 0  ?
2260                                  getTetraSplitMethod( volTool, theMethodFlags ) :
2261                                  getPrismSplitMethod( volTool, theMethodFlags, facetToSplit ));
2262     if ( splitMethod._nbSplits < 1 ) continue;
2263
2264     // find submesh to add new tetras to
2265     if ( !subMesh || !subMesh->Contains( elem ))
2266     {
2267       int shapeID = FindShape( elem );
2268       helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
2269       subMesh = GetMeshDS()->MeshElements( shapeID );
2270     }
2271     int iQ;
2272     if ( elem->IsQuadratic() )
2273     {
2274       iQ = 2;
2275       // add quadratic links to the helper
2276       for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2277       {
2278         const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
2279         int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
2280         for ( int iN = 0; iN < nbN; iN += iQ )
2281           helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
2282       }
2283       helper.SetIsQuadratic( true );
2284     }
2285     else
2286     {
2287       iQ = 1;
2288       helper.SetIsQuadratic( false );
2289     }
2290     vector<const SMDS_MeshNode*> nodes( volTool.GetNodes(),
2291                                         volTool.GetNodes() + elem->NbNodes() );
2292     helper.SetElementsOnShape( true );
2293     if ( splitMethod._baryNode )
2294     {
2295       // make a node at barycenter
2296       volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
2297       SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
2298       nodes.push_back( gcNode );
2299       newNodes.Append( gcNode );
2300     }
2301     if ( !splitMethod._faceBaryNode.empty() )
2302     {
2303       // make or find baricentric nodes of faces
2304       map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
2305       for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
2306       {
2307         map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
2308           volFace2BaryNode.insert
2309           ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
2310         if ( !f_n->second )
2311         {
2312           volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
2313           newNodes.Append( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
2314         }
2315         nodes.push_back( iF_n->second = f_n->second );
2316       }
2317     }
2318
2319     // make new volumes
2320     splitVols.resize( splitMethod._nbSplits ); // splits of a volume
2321     const int* volConn = splitMethod._connectivity;
2322     if ( splitMethod._nbCorners == 4 ) // tetra
2323       for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2324         newElems.Append( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2325                                                             nodes[ volConn[1] ],
2326                                                             nodes[ volConn[2] ],
2327                                                             nodes[ volConn[3] ]));
2328     else // prisms
2329       for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2330         newElems.Append( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2331                                                             nodes[ volConn[1] ],
2332                                                             nodes[ volConn[2] ],
2333                                                             nodes[ volConn[3] ],
2334                                                             nodes[ volConn[4] ],
2335                                                             nodes[ volConn[5] ]));
2336
2337     ReplaceElemInGroups( elem, splitVols, GetMeshDS() );
2338
2339     // Split faces on sides of the split volume
2340
2341     const SMDS_MeshNode** volNodes = volTool.GetNodes();
2342     for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2343     {
2344       const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
2345       if ( nbNodes < 4 ) continue;
2346
2347       // find an existing face
2348       vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
2349                                            volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
2350       while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
2351                                                                        /*noMedium=*/false))
2352       {
2353         // make triangles
2354         helper.SetElementsOnShape( false );
2355         vector< const SMDS_MeshElement* > triangles;
2356
2357         // find submesh to add new triangles in
2358         if ( !fSubMesh || !fSubMesh->Contains( face ))
2359         {
2360           int shapeID = FindShape( face );
2361           fSubMesh = GetMeshDS()->MeshElements( shapeID );
2362         }
2363         map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
2364         if ( iF_n != splitMethod._faceBaryNode.end() )
2365         {
2366           const SMDS_MeshNode *baryNode = iF_n->second;
2367           for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
2368           {
2369             const SMDS_MeshNode* n1 = fNodes[iN];
2370             const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
2371             const SMDS_MeshNode *n3 = baryNode;
2372             if ( !volTool.IsFaceExternal( iF ))
2373               swap( n2, n3 );
2374             triangles.push_back( helper.AddFace( n1,n2,n3 ));
2375           }
2376           if ( fSubMesh ) // update position of the bary node on geometry
2377           {
2378             if ( subMesh )
2379               subMesh->RemoveNode( baryNode, false );
2380             GetMeshDS()->SetNodeOnFace( baryNode, fSubMesh->GetID() );
2381             const TopoDS_Shape& s = GetMeshDS()->IndexToShape( fSubMesh->GetID() );
2382             if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE )
2383             {
2384               fHelper.SetSubShape( s );
2385               gp_XY uv( 1e100, 1e100 );
2386               double distXYZ[4];
2387               if ( !fHelper.CheckNodeUV( TopoDS::Face( s ), baryNode,
2388                                         uv, /*tol=*/1e-7, /*force=*/true, distXYZ ) &&
2389                    uv.X() < 1e100 )
2390               {
2391                 // node is too far from the surface
2392                 GetMeshDS()->MoveNode( baryNode, distXYZ[1], distXYZ[2], distXYZ[3] );
2393                 const_cast<SMDS_MeshNode*>( baryNode )->SetPosition
2394                   ( SMDS_PositionPtr( new SMDS_FacePosition( uv.X(), uv.Y() )));
2395               }
2396             }
2397           }
2398         }
2399         else
2400         {
2401           // among possible triangles create ones discribed by split method
2402           const int* nInd = volTool.GetFaceNodesIndices( iF );
2403           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2404           int iCom = 0; // common node of triangle faces to split into
2405           list< TTriangleFacet > facets;
2406           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
2407           {
2408             TTriangleFacet t012( nInd[ iQ * ( iCom                )],
2409                                  nInd[ iQ * ( (iCom+1)%nbNodes )],
2410                                  nInd[ iQ * ( (iCom+2)%nbNodes )]);
2411             TTriangleFacet t023( nInd[ iQ * ( iCom                )],
2412                                  nInd[ iQ * ( (iCom+2)%nbNodes )],
2413                                  nInd[ iQ * ( (iCom+3)%nbNodes )]);
2414             if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
2415             {
2416               facets.push_back( t012 );
2417               facets.push_back( t023 );
2418               for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
2419                 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom             )],
2420                                                   nInd[ iQ * ((iLast-1)%nbNodes )],
2421                                                   nInd[ iQ * ((iLast  )%nbNodes )]));
2422               break;
2423             }
2424           }
2425           list< TTriangleFacet >::iterator facet = facets.begin();
2426           if ( facet == facets.end() )
2427             break;
2428           for ( ; facet != facets.end(); ++facet )
2429           {
2430             if ( !volTool.IsFaceExternal( iF ))
2431               swap( facet->_n2, facet->_n3 );
2432             triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
2433                                                  volNodes[ facet->_n2 ],
2434                                                  volNodes[ facet->_n3 ]));
2435           }
2436         }
2437         for ( size_t i = 0; i < triangles.size(); ++i )
2438         {
2439           if ( !triangles[ i ]) continue;
2440           if ( fSubMesh )
2441             fSubMesh->AddElement( triangles[ i ]);
2442           newElems.Append( triangles[ i ]);
2443         }
2444         ReplaceElemInGroups( face, triangles, GetMeshDS() );
2445         GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2446
2447       } // while a face based on facet nodes exists
2448     } // loop on volume faces to split them into triangles
2449
2450     GetMeshDS()->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2451
2452     if ( geomType == SMDSEntity_TriQuad_Hexa )
2453     {
2454       // remove medium nodes that could become free
2455       for ( int i = 20; i < volTool.NbNodes(); ++i )
2456         if ( volNodes[i]->NbInverseElements() == 0 )
2457           GetMeshDS()->RemoveNode( volNodes[i] );
2458     }
2459   } // loop on volumes to split
2460
2461   myLastCreatedNodes = newNodes;
2462   myLastCreatedElems = newElems;
2463 }
2464
2465 //=======================================================================
2466 //function : GetHexaFacetsToSplit
2467 //purpose  : For hexahedra that will be split into prisms, finds facets to
2468 //           split into triangles. Only hexahedra adjacent to the one closest
2469 //           to theFacetNormal.Location() are returned.
2470 //param [in,out] theHexas - the hexahedra
2471 //param [in]     theFacetNormal - facet normal
2472 //param [out]    theFacets - the hexahedra and found facet IDs
2473 //=======================================================================
2474
2475 void SMESH_MeshEditor::GetHexaFacetsToSplit( TIDSortedElemSet& theHexas,
2476                                              const gp_Ax1&     theFacetNormal,
2477                                              TFacetOfElem &    theFacets)
2478 {
2479   #define THIS_METHOD "SMESH_MeshEditor::GetHexaFacetsToSplit(): "
2480
2481   // Find a hexa closest to the location of theFacetNormal
2482
2483   const SMDS_MeshElement* startHex;
2484   {
2485     // get SMDS_ElemIteratorPtr on theHexas
2486     typedef const SMDS_MeshElement*                                      TValue;
2487     typedef TIDSortedElemSet::iterator                                   TSetIterator;
2488     typedef SMDS::SimpleAccessor<TValue,TSetIterator>                    TAccesor;
2489     typedef SMDS_MeshElement::GeomFilter                                 TFilter;
2490     typedef SMDS_SetIterator < TValue, TSetIterator, TAccesor, TFilter > TElemSetIter;
2491     SMDS_ElemIteratorPtr elemIt = SMDS_ElemIteratorPtr
2492       ( new TElemSetIter( theHexas.begin(),
2493                           theHexas.end(),
2494                           SMDS_MeshElement::GeomFilter( SMDSGeom_HEXA )));
2495
2496     SMESH_ElementSearcher* searcher =
2497       SMESH_MeshAlgos::GetElementSearcher( *myMesh->GetMeshDS(), elemIt );
2498
2499     startHex = searcher->FindClosestTo( theFacetNormal.Location(), SMDSAbs_Volume );
2500
2501     delete searcher;
2502
2503     if ( !startHex )
2504       throw SALOME_Exception( THIS_METHOD "startHex not found");
2505   }
2506
2507   // Select a facet of startHex by theFacetNormal
2508
2509   SMDS_VolumeTool vTool( startHex );
2510   double norm[3], dot, maxDot = 0;
2511   int facetID = -1;
2512   for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2513     if ( vTool.GetFaceNormal( iF, norm[0], norm[1], norm[2] ))
2514     {
2515       dot = Abs( theFacetNormal.Direction().Dot( gp_Dir( norm[0], norm[1], norm[2] )));
2516       if ( dot > maxDot )
2517       {
2518         facetID = iF;
2519         maxDot = dot;
2520       }
2521     }
2522   if ( facetID < 0 )
2523     throw SALOME_Exception( THIS_METHOD "facet of startHex not found");
2524
2525   // Fill theFacets starting from facetID of startHex
2526
2527   // facets used for seach of volumes adjacent to already treated ones
2528   typedef pair< TFacetOfElem::iterator, int > TElemFacets;
2529   typedef map< TVolumeFaceKey, TElemFacets  > TFacetMap;
2530   TFacetMap facetsToCheck;
2531
2532   set<const SMDS_MeshNode*> facetNodes;
2533   const SMDS_MeshElement*   curHex;
2534
2535   const bool allHex = ((int) theHexas.size() == myMesh->NbHexas() );
2536
2537   while ( startHex )
2538   {
2539     // move in two directions from startHex via facetID
2540     for ( int is2nd = 0; is2nd < 2; ++is2nd )
2541     {
2542       curHex       = startHex;
2543       int curFacet = facetID;
2544       if ( is2nd ) // do not treat startHex twice
2545       {
2546         vTool.Set( curHex );
2547         if ( vTool.IsFreeFace( curFacet, &curHex ))
2548         {
2549           curHex = 0;
2550         }
2551         else
2552         {
2553           vTool.GetFaceNodes( curFacet, facetNodes );
2554           vTool.Set( curHex );
2555           curFacet = vTool.GetFaceIndex( facetNodes );
2556         }
2557       }
2558       while ( curHex )
2559       {
2560         // store a facet to split
2561         if ( curHex->GetGeomType() != SMDSGeom_HEXA )
2562         {
2563           theFacets.insert( make_pair( curHex, -1 ));
2564           break;
2565         }
2566         if ( !allHex && !theHexas.count( curHex ))
2567           break;
2568
2569         pair< TFacetOfElem::iterator, bool > facetIt2isNew =
2570           theFacets.insert( make_pair( curHex, curFacet ));
2571         if ( !facetIt2isNew.second )
2572           break;
2573
2574         // remember not-to-split facets in facetsToCheck
2575         int oppFacet = vTool.GetOppFaceIndexOfHex( curFacet );
2576         for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2577         {
2578           if ( iF == curFacet && iF == oppFacet )
2579             continue;
2580           TVolumeFaceKey facetKey ( vTool, iF );
2581           TElemFacets    elemFacet( facetIt2isNew.first, iF );
2582           pair< TFacetMap::iterator, bool > it2isnew =
2583             facetsToCheck.insert( make_pair( facetKey, elemFacet ));
2584           if ( !it2isnew.second )
2585             facetsToCheck.erase( it2isnew.first ); // adjacent hex already checked
2586         }
2587         // pass to a volume adjacent via oppFacet
2588         if ( vTool.IsFreeFace( oppFacet, &curHex ))
2589         {
2590           curHex = 0;
2591         }
2592         else
2593         {
2594           // get a new curFacet
2595           vTool.GetFaceNodes( oppFacet, facetNodes );
2596           vTool.Set( curHex );
2597           curFacet = vTool.GetFaceIndex( facetNodes, /*hint=*/curFacet );
2598         }
2599       }
2600     } // move in two directions from startHex via facetID
2601
2602     // Find a new startHex by facetsToCheck
2603
2604     startHex = 0;
2605     facetID  = -1;
2606     TFacetMap::iterator fIt = facetsToCheck.begin();
2607     while ( !startHex && fIt != facetsToCheck.end() )
2608     {
2609       const TElemFacets&  elemFacets = fIt->second;
2610       const SMDS_MeshElement*    hex = elemFacets.first->first;
2611       int                 splitFacet = elemFacets.first->second;
2612       int               lateralFacet = elemFacets.second;
2613       facetsToCheck.erase( fIt );
2614       fIt = facetsToCheck.begin();
2615
2616       vTool.Set( hex );
2617       if ( vTool.IsFreeFace( lateralFacet, &curHex ) || 
2618            curHex->GetGeomType() != SMDSGeom_HEXA )
2619         continue;
2620       if ( !allHex && !theHexas.count( curHex ))
2621         continue;
2622
2623       startHex = curHex;
2624
2625       // find a facet of startHex to split
2626
2627       set<const SMDS_MeshNode*> lateralNodes;
2628       vTool.GetFaceNodes( lateralFacet, lateralNodes );
2629       vTool.GetFaceNodes( splitFacet,   facetNodes );
2630       int oppLateralFacet = vTool.GetOppFaceIndexOfHex( lateralFacet );
2631       vTool.Set( startHex );
2632       lateralFacet = vTool.GetFaceIndex( lateralNodes, oppLateralFacet );
2633
2634       // look for a facet of startHex having common nodes with facetNodes
2635       // but not lateralFacet
2636       for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2637       {
2638         if ( iF == lateralFacet )
2639           continue;
2640         int nbCommonNodes = 0;
2641         const SMDS_MeshNode** nn = vTool.GetFaceNodes( iF );
2642         for ( int iN = 0, nbN = vTool.NbFaceNodes( iF ); iN < nbN; ++iN )
2643           nbCommonNodes += facetNodes.count( nn[ iN ]);
2644
2645         if ( nbCommonNodes >= 2 )
2646         {
2647           facetID = iF;
2648           break;
2649         }
2650       }
2651       if ( facetID < 0 )
2652         throw SALOME_Exception( THIS_METHOD "facet of a new startHex not found");
2653     }
2654   } //   while ( startHex )
2655
2656   return;
2657 }
2658
2659 namespace
2660 {
2661   //================================================================================
2662   /*!
2663    * \brief Selects nodes of several elements according to a given interlace
2664    *  \param [in] srcNodes - nodes to select from
2665    *  \param [out] tgtNodesVec - array of nodes of several elements to fill in
2666    *  \param [in] interlace - indices of nodes for all elements
2667    *  \param [in] nbElems - nb of elements
2668    *  \param [in] nbNodes - nb of nodes in each element
2669    *  \param [in] mesh - the mesh
2670    *  \param [out] elemQueue - a list to push elements found by the selected nodes
2671    *  \param [in] type - type of elements to look for
2672    */
2673   //================================================================================
2674
2675   void selectNodes( const vector< const SMDS_MeshNode* >& srcNodes,
2676                     vector< const SMDS_MeshNode* >*       tgtNodesVec,
2677                     const int*                            interlace,
2678                     const int                             nbElems,
2679                     const int                             nbNodes,
2680                     SMESHDS_Mesh*                         mesh = 0,
2681                     list< const SMDS_MeshElement* >*      elemQueue=0,
2682                     SMDSAbs_ElementType                   type=SMDSAbs_All)
2683   {
2684     for ( int iE = 0; iE < nbElems; ++iE )
2685     {
2686       vector< const SMDS_MeshNode* >& elemNodes = tgtNodesVec[iE];
2687       const int*                         select = & interlace[iE*nbNodes];
2688       elemNodes.resize( nbNodes );
2689       for ( int iN = 0; iN < nbNodes; ++iN )
2690         elemNodes[iN] = srcNodes[ select[ iN ]];
2691     }
2692     const SMDS_MeshElement* e;
2693     if ( elemQueue )
2694       for ( int iE = 0; iE < nbElems; ++iE )
2695         if (( e = mesh->FindElement( tgtNodesVec[iE], type, /*noMedium=*/false)))
2696           elemQueue->push_back( e );
2697   }
2698 }
2699
2700 //=======================================================================
2701 /*
2702  * Split bi-quadratic elements into linear ones without creation of additional nodes
2703  *   - bi-quadratic triangle will be split into 3 linear quadrangles;
2704  *   - bi-quadratic quadrangle will be split into 4 linear quadrangles;
2705  *   - tri-quadratic hexahedron will be split into 8 linear hexahedra;
2706  *   Quadratic elements of lower dimension  adjacent to the split bi-quadratic element
2707  *   will be split in order to keep the mesh conformal.
2708  *  \param elems - elements to split
2709  */
2710 //=======================================================================
2711
2712 void SMESH_MeshEditor::SplitBiQuadraticIntoLinear(TIDSortedElemSet& theElems)
2713 {
2714   vector< const SMDS_MeshNode* > elemNodes(27), subNodes[12], splitNodes[8];
2715   vector<const SMDS_MeshElement* > splitElems;
2716   list< const SMDS_MeshElement* > elemQueue;
2717   list< const SMDS_MeshElement* >::iterator elemIt;
2718
2719   SMESHDS_Mesh * mesh = GetMeshDS();
2720   ElemFeatures *elemType, hexaType(SMDSAbs_Volume), quadType(SMDSAbs_Face), segType(SMDSAbs_Edge);
2721   int nbElems, nbNodes;
2722
2723   TIDSortedElemSet::iterator elemSetIt = theElems.begin();
2724   for ( ; elemSetIt != theElems.end(); ++elemSetIt )
2725   {
2726     elemQueue.clear();
2727     elemQueue.push_back( *elemSetIt );
2728     for ( elemIt = elemQueue.begin(); elemIt != elemQueue.end(); ++elemIt )
2729     {
2730       const SMDS_MeshElement* elem = *elemIt;
2731       switch( elem->GetEntityType() )
2732       {
2733       case SMDSEntity_TriQuad_Hexa: // HEX27
2734       {
2735         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2736         nbElems  = nbNodes = 8;
2737         elemType = & hexaType;
2738
2739         // get nodes for new elements
2740         static int vInd[8][8] = {{ 0,8,20,11,   16,21,26,24 },
2741                                  { 1,9,20,8,    17,22,26,21 },
2742                                  { 2,10,20,9,   18,23,26,22 },
2743                                  { 3,11,20,10,  19,24,26,23 },
2744                                  { 16,21,26,24, 4,12,25,15  },
2745                                  { 17,22,26,21, 5,13,25,12  },
2746                                  { 18,23,26,22, 6,14,25,13  },
2747                                  { 19,24,26,23, 7,15,25,14  }};
2748         selectNodes( elemNodes, & splitNodes[0], &vInd[0][0], nbElems, nbNodes );
2749
2750         // add boundary faces to elemQueue
2751         static int fInd[6][9] = {{ 0,1,2,3, 8,9,10,11,   20 },
2752                                  { 4,5,6,7, 12,13,14,15, 25 },
2753                                  { 0,1,5,4, 8,17,12,16,  21 },
2754                                  { 1,2,6,5, 9,18,13,17,  22 },
2755                                  { 2,3,7,6, 10,19,14,18, 23 },
2756                                  { 3,0,4,7, 11,16,15,19, 24 }};
2757         selectNodes( elemNodes, & subNodes[0], &fInd[0][0], 6,9, mesh, &elemQueue, SMDSAbs_Face );
2758
2759         // add boundary segments to elemQueue
2760         static int eInd[12][3] = {{ 0,1,8 }, { 1,2,9 }, { 2,3,10 }, { 3,0,11 },
2761                                   { 4,5,12}, { 5,6,13}, { 6,7,14 }, { 7,4,15 },
2762                                   { 0,4,16}, { 1,5,17}, { 2,6,18 }, { 3,7,19 }};
2763         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 12,3, mesh, &elemQueue, SMDSAbs_Edge );
2764         break;
2765       }
2766       case SMDSEntity_BiQuad_Triangle: // TRIA7
2767       {
2768         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2769         nbElems = 3;
2770         nbNodes = 4;
2771         elemType = & quadType;
2772
2773         // get nodes for new elements
2774         static int fInd[3][4] = {{ 0,3,6,5 }, { 1,4,6,3 }, { 2,5,6,4 }};
2775         selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2776
2777         // add boundary segments to elemQueue
2778         static int eInd[3][3] = {{ 0,1,3 }, { 1,2,4 }, { 2,0,5 }};
2779         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 3,3, mesh, &elemQueue, SMDSAbs_Edge );
2780         break;
2781       }
2782       case SMDSEntity_BiQuad_Quadrangle: // QUAD9
2783       {
2784         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2785         nbElems = 4;
2786         nbNodes = 4;
2787         elemType = & quadType;
2788
2789         // get nodes for new elements
2790         static int fInd[4][4] = {{ 0,4,8,7 }, { 1,5,8,4 }, { 2,6,8,5 }, { 3,7,8,6 }};
2791         selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2792
2793         // add boundary segments to elemQueue
2794         static int eInd[4][3] = {{ 0,1,4 }, { 1,2,5 }, { 2,3,6 }, { 3,0,7 }};
2795         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 4,3, mesh, &elemQueue, SMDSAbs_Edge );
2796         break;
2797       }
2798       case SMDSEntity_Quad_Edge:
2799       {
2800         if ( elemIt == elemQueue.begin() )
2801           continue; // an elem is in theElems
2802         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2803         nbElems = 2;
2804         nbNodes = 2;
2805         elemType = & segType;
2806
2807         // get nodes for new elements
2808         static int eInd[2][2] = {{ 0,2 }, { 2,1 }};
2809         selectNodes( elemNodes, & splitNodes[0], &eInd[0][0], nbElems, nbNodes );
2810         break;
2811       }
2812       default: continue;
2813       } // switch( elem->GetEntityType() )
2814
2815       // Create new elements
2816
2817       SMESHDS_SubMesh* subMesh = mesh->MeshElements( elem->getshapeId() );
2818
2819       splitElems.clear();
2820
2821       //elemType->SetID( elem->GetID() ); // create an elem with the same ID as a removed one
2822       mesh->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2823       //splitElems.push_back( AddElement( splitNodes[ 0 ], *elemType ));
2824       //elemType->SetID( -1 );
2825
2826       for ( int iE = 0; iE < nbElems; ++iE )
2827         splitElems.push_back( AddElement( splitNodes[ iE ], *elemType ));
2828
2829
2830       ReplaceElemInGroups( elem, splitElems, mesh );
2831
2832       if ( subMesh )
2833         for ( size_t i = 0; i < splitElems.size(); ++i )
2834           subMesh->AddElement( splitElems[i] );
2835     }
2836   }
2837 }
2838
2839 //=======================================================================
2840 //function : AddToSameGroups
2841 //purpose  : add elemToAdd to the groups the elemInGroups belongs to
2842 //=======================================================================
2843
2844 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
2845                                         const SMDS_MeshElement* elemInGroups,
2846                                         SMESHDS_Mesh *          aMesh)
2847 {
2848   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2849   if (!groups.empty()) {
2850     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2851     for ( ; grIt != groups.end(); grIt++ ) {
2852       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2853       if ( group && group->Contains( elemInGroups ))
2854         group->SMDSGroup().Add( elemToAdd );
2855     }
2856   }
2857 }
2858
2859
2860 //=======================================================================
2861 //function : RemoveElemFromGroups
2862 //purpose  : Remove removeelem to the groups the elemInGroups belongs to
2863 //=======================================================================
2864 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2865                                              SMESHDS_Mesh *          aMesh)
2866 {
2867   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2868   if (!groups.empty())
2869   {
2870     set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2871     for (; GrIt != groups.end(); GrIt++)
2872     {
2873       SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2874       if (!grp || grp->IsEmpty()) continue;
2875       grp->SMDSGroup().Remove(removeelem);
2876     }
2877   }
2878 }
2879
2880 //================================================================================
2881 /*!
2882  * \brief Replace elemToRm by elemToAdd in the all groups
2883  */
2884 //================================================================================
2885
2886 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2887                                             const SMDS_MeshElement* elemToAdd,
2888                                             SMESHDS_Mesh *          aMesh)
2889 {
2890   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2891   if (!groups.empty()) {
2892     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2893     for ( ; grIt != groups.end(); grIt++ ) {
2894       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2895       if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2896         group->SMDSGroup().Add( elemToAdd );
2897     }
2898   }
2899 }
2900
2901 //================================================================================
2902 /*!
2903  * \brief Replace elemToRm by elemToAdd in the all groups
2904  */
2905 //================================================================================
2906
2907 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement*                elemToRm,
2908                                             const vector<const SMDS_MeshElement*>& elemToAdd,
2909                                             SMESHDS_Mesh *                         aMesh)
2910 {
2911   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2912   if (!groups.empty())
2913   {
2914     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2915     for ( ; grIt != groups.end(); grIt++ ) {
2916       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2917       if ( group && group->SMDSGroup().Remove( elemToRm ) )
2918         for ( size_t i = 0; i < elemToAdd.size(); ++i )
2919           group->SMDSGroup().Add( elemToAdd[ i ] );
2920     }
2921   }
2922 }
2923
2924 //=======================================================================
2925 //function : QuadToTri
2926 //purpose  : Cut quadrangles into triangles.
2927 //           theCrit is used to select a diagonal to cut
2928 //=======================================================================
2929
2930 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2931                                   const bool         the13Diag)
2932 {
2933   myLastCreatedElems.Clear();
2934   myLastCreatedNodes.Clear();
2935
2936   SMESHDS_Mesh * aMesh = GetMeshDS();
2937
2938   Handle(Geom_Surface) surface;
2939   SMESH_MesherHelper   helper( *GetMesh() );
2940
2941   TIDSortedElemSet::iterator itElem;
2942   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
2943   {
2944     const SMDS_MeshElement* elem = *itElem;
2945     if ( !elem || elem->GetGeomType() != SMDSGeom_QUADRANGLE )
2946       continue;
2947
2948     if ( elem->NbNodes() == 4 ) {
2949       // retrieve element nodes
2950       const SMDS_MeshNode* aNodes [4];
2951       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2952       int i = 0;
2953       while ( itN->more() )
2954         aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2955
2956       int aShapeId = FindShape( elem );
2957       const SMDS_MeshElement* newElem1 = 0;
2958       const SMDS_MeshElement* newElem2 = 0;
2959       if ( the13Diag ) {
2960         newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2961         newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2962       }
2963       else {
2964         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2965         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2966       }
2967       myLastCreatedElems.Append(newElem1);
2968       myLastCreatedElems.Append(newElem2);
2969       // put a new triangle on the same shape and add to the same groups
2970       if ( aShapeId )
2971       {
2972         aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2973         aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2974       }
2975       AddToSameGroups( newElem1, elem, aMesh );
2976       AddToSameGroups( newElem2, elem, aMesh );
2977       aMesh->RemoveElement( elem );
2978     }
2979
2980     // Quadratic quadrangle
2981
2982     else if ( elem->NbNodes() >= 8 )
2983     {
2984       // get surface elem is on
2985       int aShapeId = FindShape( elem );
2986       if ( aShapeId != helper.GetSubShapeID() ) {
2987         surface.Nullify();
2988         TopoDS_Shape shape;
2989         if ( aShapeId > 0 )
2990           shape = aMesh->IndexToShape( aShapeId );
2991         if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
2992           TopoDS_Face face = TopoDS::Face( shape );
2993           surface = BRep_Tool::Surface( face );
2994           if ( !surface.IsNull() )
2995             helper.SetSubShape( shape );
2996         }
2997       }
2998
2999       const SMDS_MeshNode* aNodes [9]; aNodes[8] = 0;
3000       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3001       for ( int i = 0; itN->more(); ++i )
3002         aNodes[ i ] = static_cast<const SMDS_MeshNode*>( itN->next() );
3003
3004       const SMDS_MeshNode* centrNode = aNodes[8];
3005       if ( centrNode == 0 )
3006       {
3007         centrNode = helper.GetCentralNode( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3008                                            aNodes[4], aNodes[5], aNodes[6], aNodes[7],
3009                                            surface.IsNull() );
3010         myLastCreatedNodes.Append(centrNode);
3011       }
3012
3013       // create a new element
3014       const SMDS_MeshElement* newElem1 = 0;
3015       const SMDS_MeshElement* newElem2 = 0;
3016       if ( the13Diag ) {
3017         newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
3018                                   aNodes[6], aNodes[7], centrNode );
3019         newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
3020                                   centrNode, aNodes[4], aNodes[5] );
3021       }
3022       else {
3023         newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
3024                                   aNodes[7], aNodes[4], centrNode );
3025         newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
3026                                   centrNode, aNodes[5], aNodes[6] );
3027       }
3028       myLastCreatedElems.Append(newElem1);
3029       myLastCreatedElems.Append(newElem2);
3030       // put a new triangle on the same shape and add to the same groups
3031       if ( aShapeId )
3032       {
3033         aMesh->SetMeshElementOnShape( newElem1, aShapeId );
3034         aMesh->SetMeshElementOnShape( newElem2, aShapeId );
3035       }
3036       AddToSameGroups( newElem1, elem, aMesh );
3037       AddToSameGroups( newElem2, elem, aMesh );
3038       aMesh->RemoveElement( elem );
3039     }
3040   }
3041
3042   return true;
3043 }
3044
3045 //=======================================================================
3046 //function : getAngle
3047 //purpose  :
3048 //=======================================================================
3049
3050 double getAngle(const SMDS_MeshElement * tr1,
3051                 const SMDS_MeshElement * tr2,
3052                 const SMDS_MeshNode *    n1,
3053                 const SMDS_MeshNode *    n2)
3054 {
3055   double angle = 2. * M_PI; // bad angle
3056
3057   // get normals
3058   SMESH::Controls::TSequenceOfXYZ P1, P2;
3059   if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
3060        !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
3061     return angle;
3062   gp_Vec N1,N2;
3063   if(!tr1->IsQuadratic())
3064     N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
3065   else
3066     N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
3067   if ( N1.SquareMagnitude() <= gp::Resolution() )
3068     return angle;
3069   if(!tr2->IsQuadratic())
3070     N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
3071   else
3072     N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
3073   if ( N2.SquareMagnitude() <= gp::Resolution() )
3074     return angle;
3075
3076   // find the first diagonal node n1 in the triangles:
3077   // take in account a diagonal link orientation
3078   const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
3079   for ( int t = 0; t < 2; t++ ) {
3080     SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
3081     int i = 0, iDiag = -1;
3082     while ( it->more()) {
3083       const SMDS_MeshElement *n = it->next();
3084       if ( n == n1 || n == n2 ) {
3085         if ( iDiag < 0)
3086           iDiag = i;
3087         else {
3088           if ( i - iDiag == 1 )
3089             nFirst[ t ] = ( n == n1 ? n2 : n1 );
3090           else
3091             nFirst[ t ] = n;
3092           break;
3093         }
3094       }
3095       i++;
3096     }
3097   }
3098   if ( nFirst[ 0 ] == nFirst[ 1 ] )
3099     N2.Reverse();
3100
3101   angle = N1.Angle( N2 );
3102   //SCRUTE( angle );
3103   return angle;
3104 }
3105
3106 // =================================================
3107 // class generating a unique ID for a pair of nodes
3108 // and able to return nodes by that ID
3109 // =================================================
3110 class LinkID_Gen {
3111 public:
3112
3113   LinkID_Gen( const SMESHDS_Mesh* theMesh )
3114     :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
3115   {}
3116
3117   long GetLinkID (const SMDS_MeshNode * n1,
3118                   const SMDS_MeshNode * n2) const
3119   {
3120     return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
3121   }
3122
3123   bool GetNodes (const long             theLinkID,
3124                  const SMDS_MeshNode* & theNode1,
3125                  const SMDS_MeshNode* & theNode2) const
3126   {
3127     theNode1 = myMesh->FindNode( theLinkID / myMaxID );
3128     if ( !theNode1 ) return false;
3129     theNode2 = myMesh->FindNode( theLinkID % myMaxID );
3130     if ( !theNode2 ) return false;
3131     return true;
3132   }
3133
3134 private:
3135   LinkID_Gen();
3136   const SMESHDS_Mesh* myMesh;
3137   long                myMaxID;
3138 };
3139
3140
3141 //=======================================================================
3142 //function : TriToQuad
3143 //purpose  : Fuse neighbour triangles into quadrangles.
3144 //           theCrit is used to select a neighbour to fuse with.
3145 //           theMaxAngle is a max angle between element normals at which
3146 //           fusion is still performed.
3147 //=======================================================================
3148
3149 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet &                   theElems,
3150                                   SMESH::Controls::NumericalFunctorPtr theCrit,
3151                                   const double                         theMaxAngle)
3152 {
3153   myLastCreatedElems.Clear();
3154   myLastCreatedNodes.Clear();
3155
3156   if ( !theCrit.get() )
3157     return false;
3158
3159   SMESHDS_Mesh * aMesh = GetMeshDS();
3160
3161   // Prepare data for algo: build
3162   // 1. map of elements with their linkIDs
3163   // 2. map of linkIDs with their elements
3164
3165   map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
3166   map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
3167   map< const SMDS_MeshElement*, set< SMESH_TLink > >  mapEl_setLi;
3168   map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
3169
3170   TIDSortedElemSet::iterator itElem;
3171   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
3172   {
3173     const SMDS_MeshElement* elem = *itElem;
3174     if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
3175     bool IsTria = ( elem->NbCornerNodes()==3 );
3176     if (!IsTria) continue;
3177
3178     // retrieve element nodes
3179     const SMDS_MeshNode* aNodes [4];
3180     SMDS_NodeIteratorPtr itN = elem->nodeIterator();
3181     int i = 0;
3182     while ( i < 3 )
3183       aNodes[ i++ ] = itN->next();
3184     aNodes[ 3 ] = aNodes[ 0 ];
3185
3186     // fill maps
3187     for ( i = 0; i < 3; i++ ) {
3188       SMESH_TLink link( aNodes[i], aNodes[i+1] );
3189       // check if elements sharing a link can be fused
3190       itLE = mapLi_listEl.find( link );
3191       if ( itLE != mapLi_listEl.end() ) {
3192         if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
3193           continue;
3194         const SMDS_MeshElement* elem2 = (*itLE).second.front();
3195         //if ( FindShape( elem ) != FindShape( elem2 ))
3196         //  continue; // do not fuse triangles laying on different shapes
3197         if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
3198           continue; // avoid making badly shaped quads
3199         (*itLE).second.push_back( elem );
3200       }
3201       else {
3202         mapLi_listEl[ link ].push_back( elem );
3203       }
3204       mapEl_setLi [ elem ].insert( link );
3205     }
3206   }
3207   // Clean the maps from the links shared by a sole element, ie
3208   // links to which only one element is bound in mapLi_listEl
3209
3210   for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
3211     int nbElems = (*itLE).second.size();
3212     if ( nbElems < 2  ) {
3213       const SMDS_MeshElement* elem = (*itLE).second.front();
3214       SMESH_TLink link = (*itLE).first;
3215       mapEl_setLi[ elem ].erase( link );
3216       if ( mapEl_setLi[ elem ].empty() )
3217         mapEl_setLi.erase( elem );
3218     }
3219   }
3220
3221   // Algo: fuse triangles into quadrangles
3222
3223   while ( ! mapEl_setLi.empty() ) {
3224     // Look for the start element:
3225     // the element having the least nb of shared links
3226     const SMDS_MeshElement* startElem = 0;
3227     int minNbLinks = 4;
3228     for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
3229       int nbLinks = (*itEL).second.size();
3230       if ( nbLinks < minNbLinks ) {
3231         startElem = (*itEL).first;
3232         minNbLinks = nbLinks;
3233         if ( minNbLinks == 1 )
3234           break;
3235       }
3236     }
3237
3238     // search elements to fuse starting from startElem or links of elements
3239     // fused earlyer - startLinks
3240     list< SMESH_TLink > startLinks;
3241     while ( startElem || !startLinks.empty() ) {
3242       while ( !startElem && !startLinks.empty() ) {
3243         // Get an element to start, by a link
3244         SMESH_TLink linkId = startLinks.front();
3245         startLinks.pop_front();
3246         itLE = mapLi_listEl.find( linkId );
3247         if ( itLE != mapLi_listEl.end() ) {
3248           list< const SMDS_MeshElement* > & listElem = (*itLE).second;
3249           list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
3250           for ( ; itE != listElem.end() ; itE++ )
3251             if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
3252               startElem = (*itE);
3253           mapLi_listEl.erase( itLE );
3254         }
3255       }
3256
3257       if ( startElem ) {
3258         // Get candidates to be fused
3259         const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
3260         const SMESH_TLink *link12 = 0, *link13 = 0;
3261         startElem = 0;
3262         ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
3263         set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
3264         ASSERT( !setLi.empty() );
3265         set< SMESH_TLink >::iterator itLi;
3266         for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
3267         {
3268           const SMESH_TLink & link = (*itLi);
3269           itLE = mapLi_listEl.find( link );
3270           if ( itLE == mapLi_listEl.end() )
3271             continue;
3272
3273           const SMDS_MeshElement* elem = (*itLE).second.front();
3274           if ( elem == tr1 )
3275             elem = (*itLE).second.back();
3276           mapLi_listEl.erase( itLE );
3277           if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
3278             continue;
3279           if ( tr2 ) {
3280             tr3 = elem;
3281             link13 = &link;
3282           }
3283           else {
3284             tr2 = elem;
3285             link12 = &link;
3286           }
3287
3288           // add other links of elem to list of links to re-start from
3289           set< SMESH_TLink >& links = mapEl_setLi[ elem ];
3290           set< SMESH_TLink >::iterator it;
3291           for ( it = links.begin(); it != links.end(); it++ ) {
3292             const SMESH_TLink& link2 = (*it);
3293             if ( link2 != link )
3294               startLinks.push_back( link2 );
3295           }
3296         }
3297
3298         // Get nodes of possible quadrangles
3299         const SMDS_MeshNode *n12 [4], *n13 [4];
3300         bool Ok12 = false, Ok13 = false;
3301         const SMDS_MeshNode *linkNode1, *linkNode2;
3302         if(tr2) {
3303           linkNode1 = link12->first;
3304           linkNode2 = link12->second;
3305           if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
3306             Ok12 = true;
3307         }
3308         if(tr3) {
3309           linkNode1 = link13->first;
3310           linkNode2 = link13->second;
3311           if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
3312             Ok13 = true;
3313         }
3314
3315         // Choose a pair to fuse
3316         if ( Ok12 && Ok13 ) {
3317           SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
3318           SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
3319           double aBadRate12 = getBadRate( &quad12, theCrit );
3320           double aBadRate13 = getBadRate( &quad13, theCrit );
3321           if (  aBadRate13 < aBadRate12 )
3322             Ok12 = false;
3323           else
3324             Ok13 = false;
3325         }
3326
3327         // Make quadrangles
3328         // and remove fused elems and remove links from the maps
3329         mapEl_setLi.erase( tr1 );
3330         if ( Ok12 )
3331         {
3332           mapEl_setLi.erase( tr2 );
3333           mapLi_listEl.erase( *link12 );
3334           if ( tr1->NbNodes() == 3 )
3335           {
3336             const SMDS_MeshElement* newElem = 0;
3337             newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
3338             myLastCreatedElems.Append(newElem);
3339             AddToSameGroups( newElem, tr1, aMesh );
3340             int aShapeId = tr1->getshapeId();
3341             if ( aShapeId )
3342               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3343             aMesh->RemoveElement( tr1 );
3344             aMesh->RemoveElement( tr2 );
3345           }
3346           else {
3347             vector< const SMDS_MeshNode* > N1;
3348             vector< const SMDS_MeshNode* > N2;
3349             getNodesFromTwoTria(tr1,tr2,N1,N2);
3350             // now we receive following N1 and N2 (using numeration as in image in InverseDiag())
3351             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
3352             // i.e. first nodes from both arrays form a new diagonal
3353             const SMDS_MeshNode* aNodes[8];
3354             aNodes[0] = N1[0];
3355             aNodes[1] = N1[1];
3356             aNodes[2] = N2[0];
3357             aNodes[3] = N2[1];
3358             aNodes[4] = N1[3];
3359             aNodes[5] = N2[5];
3360             aNodes[6] = N2[3];
3361             aNodes[7] = N1[5];
3362             const SMDS_MeshElement* newElem = 0;
3363             if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3364               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3365                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3366             else
3367               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3368                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3369             myLastCreatedElems.Append(newElem);
3370             AddToSameGroups( newElem, tr1, aMesh );
3371             int aShapeId = tr1->getshapeId();
3372             if ( aShapeId )
3373               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3374             aMesh->RemoveElement( tr1 );
3375             aMesh->RemoveElement( tr2 );
3376             // remove middle node (9)
3377             if ( N1[4]->NbInverseElements() == 0 )
3378               aMesh->RemoveNode( N1[4] );
3379             if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3380               aMesh->RemoveNode( N1[6] );
3381             if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3382               aMesh->RemoveNode( N2[6] );
3383           }
3384         }
3385         else if ( Ok13 )
3386         {
3387           mapEl_setLi.erase( tr3 );
3388           mapLi_listEl.erase( *link13 );
3389           if ( tr1->NbNodes() == 3 ) {
3390             const SMDS_MeshElement* newElem = 0;
3391             newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
3392             myLastCreatedElems.Append(newElem);
3393             AddToSameGroups( newElem, tr1, aMesh );
3394             int aShapeId = tr1->getshapeId();
3395             if ( aShapeId )
3396               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3397             aMesh->RemoveElement( tr1 );
3398             aMesh->RemoveElement( tr3 );
3399           }
3400           else {
3401             vector< const SMDS_MeshNode* > N1;
3402             vector< const SMDS_MeshNode* > N2;
3403             getNodesFromTwoTria(tr1,tr3,N1,N2);
3404             // now we receive following N1 and N2 (using numeration as above image)
3405             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
3406             // i.e. first nodes from both arrays form a new diagonal
3407             const SMDS_MeshNode* aNodes[8];
3408             aNodes[0] = N1[0];
3409             aNodes[1] = N1[1];
3410             aNodes[2] = N2[0];
3411             aNodes[3] = N2[1];
3412             aNodes[4] = N1[3];
3413             aNodes[5] = N2[5];
3414             aNodes[6] = N2[3];
3415             aNodes[7] = N1[5];
3416             const SMDS_MeshElement* newElem = 0;
3417             if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3418               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3419                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3420             else
3421               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3422                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3423             myLastCreatedElems.Append(newElem);
3424             AddToSameGroups( newElem, tr1, aMesh );
3425             int aShapeId = tr1->getshapeId();
3426             if ( aShapeId )
3427               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3428             aMesh->RemoveElement( tr1 );
3429             aMesh->RemoveElement( tr3 );
3430             // remove middle node (9)
3431             if ( N1[4]->NbInverseElements() == 0 )
3432               aMesh->RemoveNode( N1[4] );
3433             if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3434               aMesh->RemoveNode( N1[6] );
3435             if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3436               aMesh->RemoveNode( N2[6] );
3437           }
3438         }
3439
3440         // Next element to fuse: the rejected one
3441         if ( tr3 )
3442           startElem = Ok12 ? tr3 : tr2;
3443
3444       } // if ( startElem )
3445     } // while ( startElem || !startLinks.empty() )
3446   } // while ( ! mapEl_setLi.empty() )
3447
3448   return true;
3449 }
3450
3451
3452 /*#define DUMPSO(txt) \
3453 //  cout << txt << endl;
3454 //=============================================================================
3455 //
3456 //
3457 //
3458 //=============================================================================
3459 static void swap( int i1, int i2, int idNodes[], gp_Pnt P[] )
3460 {
3461 if ( i1 == i2 )
3462 return;
3463 int tmp = idNodes[ i1 ];
3464 idNodes[ i1 ] = idNodes[ i2 ];
3465 idNodes[ i2 ] = tmp;
3466 gp_Pnt Ptmp = P[ i1 ];
3467 P[ i1 ] = P[ i2 ];
3468 P[ i2 ] = Ptmp;
3469 DUMPSO( i1 << "(" << idNodes[ i2 ] << ") <-> " << i2 << "(" << idNodes[ i1 ] << ")");
3470 }
3471
3472 //=======================================================================
3473 //function : SortQuadNodes
3474 //purpose  : Set 4 nodes of a quadrangle face in a good order.
3475 //           Swap 1<->2 or 2<->3 nodes and correspondingly return
3476 //           1 or 2 else 0.
3477 //=======================================================================
3478
3479 int SMESH_MeshEditor::SortQuadNodes (const SMDS_Mesh * theMesh,
3480 int               idNodes[] )
3481 {
3482   gp_Pnt P[4];
3483   int i;
3484   for ( i = 0; i < 4; i++ ) {
3485     const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
3486     if ( !n ) return 0;
3487     P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
3488   }
3489
3490   gp_Vec V1(P[0], P[1]);
3491   gp_Vec V2(P[0], P[2]);
3492   gp_Vec V3(P[0], P[3]);
3493
3494   gp_Vec Cross1 = V1 ^ V2;
3495   gp_Vec Cross2 = V2 ^ V3;
3496
3497   i = 0;
3498   if (Cross1.Dot(Cross2) < 0)
3499   {
3500     Cross1 = V2 ^ V1;
3501     Cross2 = V1 ^ V3;
3502
3503     if (Cross1.Dot(Cross2) < 0)
3504       i = 2;
3505     else
3506       i = 1;
3507     swap ( i, i + 1, idNodes, P );
3508
3509     //     for ( int ii = 0; ii < 4; ii++ ) {
3510     //       const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
3511     //       DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3512     //     }
3513   }
3514   return i;
3515 }
3516
3517 //=======================================================================
3518 //function : SortHexaNodes
3519 //purpose  : Set 8 nodes of a hexahedron in a good order.
3520 //           Return success status
3521 //=======================================================================
3522
3523 bool SMESH_MeshEditor::SortHexaNodes (const SMDS_Mesh * theMesh,
3524                                       int               idNodes[] )
3525 {
3526   gp_Pnt P[8];
3527   int i;
3528   DUMPSO( "INPUT: ========================================");
3529   for ( i = 0; i < 8; i++ ) {
3530     const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
3531     if ( !n ) return false;
3532     P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
3533     DUMPSO( i << "(" << idNodes[i] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3534   }
3535   DUMPSO( "========================================");
3536
3537
3538   set<int> faceNodes;  // ids of bottom face nodes, to be found
3539   set<int> checkedId1; // ids of tried 2-nd nodes
3540   Standard_Real leastDist = DBL_MAX; // dist of the 4-th node from 123 plane
3541   const Standard_Real tol = 1.e-6;   // tolerance to find nodes in plane
3542   int iMin, iLoop1 = 0;
3543
3544   // Loop to try the 2-nd nodes
3545
3546   while ( leastDist > DBL_MIN && ++iLoop1 < 8 )
3547   {
3548     // Find not checked 2-nd node
3549     for ( i = 1; i < 8; i++ )
3550       if ( checkedId1.find( idNodes[i] ) == checkedId1.end() ) {
3551         int id1 = idNodes[i];
3552         swap ( 1, i, idNodes, P );
3553         checkedId1.insert ( id1 );
3554         break;
3555       }
3556
3557     // Find the 3-d node so that 1-2-3 triangle to be on a hexa face,
3558     // ie that all but meybe one (id3 which is on the same face) nodes
3559     // lay on the same side from the triangle plane.
3560
3561     bool manyInPlane = false; // more than 4 nodes lay in plane
3562     int iLoop2 = 0;
3563     while ( ++iLoop2 < 6 ) {
3564
3565       // get 1-2-3 plane coeffs
3566       Standard_Real A, B, C, D;
3567       gp_Vec N = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
3568       if ( N.SquareMagnitude() > gp::Resolution() )
3569       {
3570         gp_Pln pln ( P[0], N );
3571         pln.Coefficients( A, B, C, D );
3572
3573         // find the node (iMin) closest to pln
3574         Standard_Real dist[ 8 ], minDist = DBL_MAX;
3575         set<int> idInPln;
3576         for ( i = 3; i < 8; i++ ) {
3577           dist[i] = A * P[i].X() + B * P[i].Y() + C * P[i].Z() + D;
3578           if ( fabs( dist[i] ) < minDist ) {
3579             minDist = fabs( dist[i] );
3580             iMin = i;
3581           }
3582           if ( fabs( dist[i] ) <= tol )
3583             idInPln.insert( idNodes[i] );
3584         }
3585
3586         // there should not be more than 4 nodes in bottom plane
3587         if ( idInPln.size() > 1 )
3588         {
3589           DUMPSO( "### idInPln.size() = " << idInPln.size());
3590           // idInPlane does not contain the first 3 nodes
3591           if ( manyInPlane || idInPln.size() == 5)
3592             return false; // all nodes in one plane
3593           manyInPlane = true;
3594
3595           // set the 1-st node to be not in plane
3596           for ( i = 3; i < 8; i++ ) {
3597             if ( idInPln.find( idNodes[ i ] ) == idInPln.end() ) {
3598               DUMPSO( "### Reset 0-th node");
3599               swap( 0, i, idNodes, P );
3600               break;
3601             }
3602           }
3603
3604           // reset to re-check second nodes
3605           leastDist = DBL_MAX;
3606           faceNodes.clear();
3607           checkedId1.clear();
3608           iLoop1 = 0;
3609           break; // from iLoop2;
3610         }
3611
3612         // check that the other 4 nodes are on the same side
3613         bool sameSide = true;
3614         bool isNeg = dist[ iMin == 3 ? 4 : 3 ] <= 0.;
3615         for ( i = 3; sameSide && i < 8; i++ ) {
3616           if ( i != iMin )
3617             sameSide = ( isNeg == dist[i] <= 0.);
3618         }
3619
3620         // keep best solution
3621         if ( sameSide && minDist < leastDist ) {
3622           leastDist = minDist;
3623           faceNodes.clear();
3624           faceNodes.insert( idNodes[ 1 ] );
3625           faceNodes.insert( idNodes[ 2 ] );
3626           faceNodes.insert( idNodes[ iMin ] );
3627           DUMPSO( "loop " << iLoop2 << " id2 " << idNodes[ 1 ] << " id3 " << idNodes[ 2 ]
3628                   << " leastDist = " << leastDist);
3629           if ( leastDist <= DBL_MIN )
3630             break;
3631         }
3632       }
3633
3634       // set next 3-d node to check
3635       int iNext = 2 + iLoop2;
3636       if ( iNext < 8 ) {
3637         DUMPSO( "Try 2-nd");
3638         swap ( 2, iNext, idNodes, P );
3639       }
3640     } // while ( iLoop2 < 6 )
3641   } // iLoop1
3642
3643   if ( faceNodes.empty() ) return false;
3644
3645   // Put the faceNodes in proper places
3646   for ( i = 4; i < 8; i++ ) {
3647     if ( faceNodes.find( idNodes[ i ] ) != faceNodes.end() ) {
3648       // find a place to put
3649       int iTo = 1;
3650       while ( faceNodes.find( idNodes[ iTo ] ) != faceNodes.end() )
3651         iTo++;
3652       DUMPSO( "Set faceNodes");
3653       swap ( iTo, i, idNodes, P );
3654     }
3655   }
3656
3657
3658   // Set nodes of the found bottom face in good order
3659   DUMPSO( " Found bottom face: ");
3660   i = SortQuadNodes( theMesh, idNodes );
3661   if ( i ) {
3662     gp_Pnt Ptmp = P[ i ];
3663     P[ i ] = P[ i+1 ];
3664     P[ i+1 ] = Ptmp;
3665   }
3666   //   else
3667   //     for ( int ii = 0; ii < 4; ii++ ) {
3668   //       const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
3669   //       DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3670   //    }
3671
3672   // Gravity center of the top and bottom faces
3673   gp_Pnt aGCb = ( P[0].XYZ() + P[1].XYZ() + P[2].XYZ() + P[3].XYZ() ) / 4.;
3674   gp_Pnt aGCt = ( P[4].XYZ() + P[5].XYZ() + P[6].XYZ() + P[7].XYZ() ) / 4.;
3675
3676   // Get direction from the bottom to the top face
3677   gp_Vec upDir ( aGCb, aGCt );
3678   Standard_Real upDirSize = upDir.Magnitude();
3679   if ( upDirSize <= gp::Resolution() ) return false;
3680   upDir / upDirSize;
3681
3682   // Assure that the bottom face normal points up
3683   gp_Vec Nb = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
3684   Nb += gp_Vec (P[0], P[2]).Crossed( gp_Vec (P[0], P[3]) );
3685   if ( Nb.Dot( upDir ) < 0 ) {
3686     DUMPSO( "Reverse bottom face");
3687     swap( 1, 3, idNodes, P );
3688   }
3689
3690   // Find 5-th node - the one closest to the 1-st among the last 4 nodes.
3691   Standard_Real minDist = DBL_MAX;
3692   for ( i = 4; i < 8; i++ ) {
3693     // projection of P[i] to the plane defined by P[0] and upDir
3694     gp_Pnt Pp = P[i].Translated( upDir * ( upDir.Dot( gp_Vec( P[i], P[0] ))));
3695     Standard_Real sqDist = P[0].SquareDistance( Pp );
3696     if ( sqDist < minDist ) {
3697       minDist = sqDist;
3698       iMin = i;
3699     }
3700   }
3701   DUMPSO( "Set 4-th");
3702   swap ( 4, iMin, idNodes, P );
3703
3704   // Set nodes of the top face in good order
3705   DUMPSO( "Sort top face");
3706   i = SortQuadNodes( theMesh, &idNodes[4] );
3707   if ( i ) {
3708     i += 4;
3709     gp_Pnt Ptmp = P[ i ];
3710     P[ i ] = P[ i+1 ];
3711     P[ i+1 ] = Ptmp;
3712   }
3713
3714   // Assure that direction of the top face normal is from the bottom face
3715   gp_Vec Nt = gp_Vec (P[4], P[5]).Crossed( gp_Vec (P[4], P[6]) );
3716   Nt += gp_Vec (P[4], P[6]).Crossed( gp_Vec (P[4], P[7]) );
3717   if ( Nt.Dot( upDir ) < 0 ) {
3718     DUMPSO( "Reverse top face");
3719     swap( 5, 7, idNodes, P );
3720   }
3721
3722   //   DUMPSO( "OUTPUT: ========================================");
3723   //   for ( i = 0; i < 8; i++ ) {
3724   //     float *p = ugrid->GetPoint(idNodes[i]);
3725   //     DUMPSO( i << "(" << idNodes[i] << ") : " << p[0] << " " << p[1] << " " << p[2]);
3726   //   }
3727
3728   return true;
3729 }*/
3730
3731 //================================================================================
3732 /*!
3733  * \brief Return nodes linked to the given one
3734  * \param theNode - the node
3735  * \param linkedNodes - the found nodes
3736  * \param type - the type of elements to check
3737  *
3738  * Medium nodes are ignored
3739  */
3740 //================================================================================
3741
3742 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
3743                                        TIDSortedElemSet &   linkedNodes,
3744                                        SMDSAbs_ElementType  type )
3745 {
3746   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
3747   while ( elemIt->more() )
3748   {
3749     const SMDS_MeshElement* elem = elemIt->next();
3750     if(elem->GetType() == SMDSAbs_0DElement)
3751       continue;
3752
3753     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
3754     if ( elem->GetType() == SMDSAbs_Volume )
3755     {
3756       SMDS_VolumeTool vol( elem );
3757       while ( nodeIt->more() ) {
3758         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3759         if ( theNode != n && vol.IsLinked( theNode, n ))
3760           linkedNodes.insert( n );
3761       }
3762     }
3763     else
3764     {
3765       for ( int i = 0; nodeIt->more(); ++i ) {
3766         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3767         if ( n == theNode ) {
3768           int iBefore = i - 1;
3769           int iAfter  = i + 1;
3770           if ( elem->IsQuadratic() ) {
3771             int nb = elem->NbNodes() / 2;
3772             iAfter  = SMESH_MesherHelper::WrapIndex( iAfter, nb );
3773             iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
3774           }
3775           linkedNodes.insert( elem->GetNodeWrap( iAfter ));
3776           linkedNodes.insert( elem->GetNodeWrap( iBefore ));
3777         }
3778       }
3779     }
3780   }
3781 }
3782
3783 //=======================================================================
3784 //function : laplacianSmooth
3785 //purpose  : pulls theNode toward the center of surrounding nodes directly
3786 //           connected to that node along an element edge
3787 //=======================================================================
3788
3789 void laplacianSmooth(const SMDS_MeshNode*                 theNode,
3790                      const Handle(Geom_Surface)&          theSurface,
3791                      map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3792 {
3793   // find surrounding nodes
3794
3795   TIDSortedElemSet nodeSet;
3796   SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
3797
3798   // compute new coodrs
3799
3800   double coord[] = { 0., 0., 0. };
3801   TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3802   for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
3803     const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3804     if ( theSurface.IsNull() ) { // smooth in 3D
3805       coord[0] += node->X();
3806       coord[1] += node->Y();
3807       coord[2] += node->Z();
3808     }
3809     else { // smooth in 2D
3810       ASSERT( theUVMap.find( node ) != theUVMap.end() );
3811       gp_XY* uv = theUVMap[ node ];
3812       coord[0] += uv->X();
3813       coord[1] += uv->Y();
3814     }
3815   }
3816   int nbNodes = nodeSet.size();
3817   if ( !nbNodes )
3818     return;
3819   coord[0] /= nbNodes;
3820   coord[1] /= nbNodes;
3821
3822   if ( !theSurface.IsNull() ) {
3823     ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3824     theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3825     gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3826     coord[0] = p3d.X();
3827     coord[1] = p3d.Y();
3828     coord[2] = p3d.Z();
3829   }
3830   else
3831     coord[2] /= nbNodes;
3832
3833   // move node
3834
3835   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3836 }
3837
3838 //=======================================================================
3839 //function : centroidalSmooth
3840 //purpose  : pulls theNode toward the element-area-weighted centroid of the
3841 //           surrounding elements
3842 //=======================================================================
3843
3844 void centroidalSmooth(const SMDS_MeshNode*                 theNode,
3845                       const Handle(Geom_Surface)&          theSurface,
3846                       map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3847 {
3848   gp_XYZ aNewXYZ(0.,0.,0.);
3849   SMESH::Controls::Area anAreaFunc;
3850   double totalArea = 0.;
3851   int nbElems = 0;
3852
3853   // compute new XYZ
3854
3855   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3856   while ( elemIt->more() )
3857   {
3858     const SMDS_MeshElement* elem = elemIt->next();
3859     nbElems++;
3860
3861     gp_XYZ elemCenter(0.,0.,0.);
3862     SMESH::Controls::TSequenceOfXYZ aNodePoints;
3863     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3864     int nn = elem->NbNodes();
3865     if(elem->IsQuadratic()) nn = nn/2;
3866     int i=0;
3867     //while ( itN->more() ) {
3868     while ( i<nn ) {
3869       const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3870       i++;
3871       gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3872       aNodePoints.push_back( aP );
3873       if ( !theSurface.IsNull() ) { // smooth in 2D
3874         ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3875         gp_XY* uv = theUVMap[ aNode ];
3876         aP.SetCoord( uv->X(), uv->Y(), 0. );
3877       }
3878       elemCenter += aP;
3879     }
3880     double elemArea = anAreaFunc.GetValue( aNodePoints );
3881     totalArea += elemArea;
3882     elemCenter /= nn;
3883     aNewXYZ += elemCenter * elemArea;
3884   }
3885   aNewXYZ /= totalArea;
3886   if ( !theSurface.IsNull() ) {
3887     theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3888     aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3889   }
3890
3891   // move node
3892
3893   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3894 }
3895
3896 //=======================================================================
3897 //function : getClosestUV
3898 //purpose  : return UV of closest projection
3899 //=======================================================================
3900
3901 static bool getClosestUV (Extrema_GenExtPS& projector,
3902                           const gp_Pnt&     point,
3903                           gp_XY &           result)
3904 {
3905   projector.Perform( point );
3906   if ( projector.IsDone() ) {
3907     double u, v, minVal = DBL_MAX;
3908     for ( int i = projector.NbExt(); i > 0; i-- )
3909       if ( projector.SquareDistance( i ) < minVal ) {
3910         minVal = projector.SquareDistance( i );
3911         projector.Point( i ).Parameter( u, v );
3912       }
3913     result.SetCoord( u, v );
3914     return true;
3915   }
3916   return false;
3917 }
3918
3919 //=======================================================================
3920 //function : Smooth
3921 //purpose  : Smooth theElements during theNbIterations or until a worst
3922 //           element has aspect ratio <= theTgtAspectRatio.
3923 //           Aspect Ratio varies in range [1.0, inf].
3924 //           If theElements is empty, the whole mesh is smoothed.
3925 //           theFixedNodes contains additionally fixed nodes. Nodes built
3926 //           on edges and boundary nodes are always fixed.
3927 //=======================================================================
3928
3929 void SMESH_MeshEditor::Smooth (TIDSortedElemSet &          theElems,
3930                                set<const SMDS_MeshNode*> & theFixedNodes,
3931                                const SmoothMethod          theSmoothMethod,
3932                                const int                   theNbIterations,
3933                                double                      theTgtAspectRatio,
3934                                const bool                  the2D)
3935 {
3936   myLastCreatedElems.Clear();
3937   myLastCreatedNodes.Clear();
3938
3939   if ( theTgtAspectRatio < 1.0 )
3940     theTgtAspectRatio = 1.0;
3941
3942   const double disttol = 1.e-16;
3943
3944   SMESH::Controls::AspectRatio aQualityFunc;
3945
3946   SMESHDS_Mesh* aMesh = GetMeshDS();
3947
3948   if ( theElems.empty() ) {
3949     // add all faces to theElems
3950     SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3951     while ( fIt->more() ) {
3952       const SMDS_MeshElement* face = fIt->next();
3953       theElems.insert( theElems.end(), face );
3954     }
3955   }
3956   // get all face ids theElems are on
3957   set< int > faceIdSet;
3958   TIDSortedElemSet::iterator itElem;
3959   if ( the2D )
3960     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3961       int fId = FindShape( *itElem );
3962       // check that corresponding submesh exists and a shape is face
3963       if (fId &&
3964           faceIdSet.find( fId ) == faceIdSet.end() &&
3965           aMesh->MeshElements( fId )) {
3966         TopoDS_Shape F = aMesh->IndexToShape( fId );
3967         if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3968           faceIdSet.insert( fId );
3969       }
3970     }
3971   faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3972
3973   // ===============================================
3974   // smooth elements on each TopoDS_Face separately
3975   // ===============================================
3976
3977   SMESH_MesherHelper helper( *GetMesh() );
3978
3979   set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treat 0 fId at the end
3980   for ( ; fId != faceIdSet.rend(); ++fId )
3981   {
3982     // get face surface and submesh
3983     Handle(Geom_Surface) surface;
3984     SMESHDS_SubMesh* faceSubMesh = 0;
3985     TopoDS_Face face;
3986     double fToler2 = 0;
3987     double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3988     bool isUPeriodic = false, isVPeriodic = false;
3989     if ( *fId )
3990     {
3991       face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3992       surface = BRep_Tool::Surface( face );
3993       faceSubMesh = aMesh->MeshElements( *fId );
3994       fToler2 = BRep_Tool::Tolerance( face );
3995       fToler2 *= fToler2 * 10.;
3996       isUPeriodic = surface->IsUPeriodic();
3997       // if ( isUPeriodic )
3998       //   surface->UPeriod();
3999       isVPeriodic = surface->IsVPeriodic();
4000       // if ( isVPeriodic )
4001       //   surface->VPeriod();
4002       surface->Bounds( u1, u2, v1, v2 );
4003       helper.SetSubShape( face );
4004     }
4005     // ---------------------------------------------------------
4006     // for elements on a face, find movable and fixed nodes and
4007     // compute UV for them
4008     // ---------------------------------------------------------
4009     bool checkBoundaryNodes = false;
4010     bool isQuadratic = false;
4011     set<const SMDS_MeshNode*> setMovableNodes;
4012     map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
4013     list< gp_XY > listUV; // uvs the 2 uvMaps refer to
4014     list< const SMDS_MeshElement* > elemsOnFace;
4015
4016     Extrema_GenExtPS projector;
4017     GeomAdaptor_Surface surfAdaptor;
4018     if ( !surface.IsNull() ) {
4019       surfAdaptor.Load( surface );
4020       projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
4021     }
4022     int nbElemOnFace = 0;
4023     itElem = theElems.begin();
4024     // loop on not yet smoothed elements: look for elems on a face
4025     while ( itElem != theElems.end() )
4026     {
4027       if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
4028         break; // all elements found
4029
4030       const SMDS_MeshElement* elem = *itElem;
4031       if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
4032            ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
4033         ++itElem;
4034         continue;
4035       }
4036       elemsOnFace.push_back( elem );
4037       theElems.erase( itElem++ );
4038       nbElemOnFace++;
4039
4040       if ( !isQuadratic )
4041         isQuadratic = elem->IsQuadratic();
4042
4043       // get movable nodes of elem
4044       const SMDS_MeshNode* node;
4045       SMDS_TypeOfPosition posType;
4046       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4047       int nn = 0, nbn =  elem->NbNodes();
4048       if(elem->IsQuadratic())
4049         nbn = nbn/2;
4050       while ( nn++ < nbn ) {
4051         node = static_cast<const SMDS_MeshNode*>( itN->next() );
4052         const SMDS_PositionPtr& pos = node->GetPosition();
4053         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
4054         if (posType != SMDS_TOP_EDGE &&
4055             posType != SMDS_TOP_VERTEX &&
4056             theFixedNodes.find( node ) == theFixedNodes.end())
4057         {
4058           // check if all faces around the node are on faceSubMesh
4059           // because a node on edge may be bound to face
4060           bool all = true;
4061           if ( faceSubMesh ) {
4062             SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
4063             while ( eIt->more() && all ) {
4064               const SMDS_MeshElement* e = eIt->next();
4065               all = faceSubMesh->Contains( e );
4066             }
4067           }
4068           if ( all )
4069             setMovableNodes.insert( node );
4070           else
4071             checkBoundaryNodes = true;
4072         }
4073         if ( posType == SMDS_TOP_3DSPACE )
4074           checkBoundaryNodes = true;
4075       }
4076
4077       if ( surface.IsNull() )
4078         continue;
4079
4080       // get nodes to check UV
4081       list< const SMDS_MeshNode* > uvCheckNodes;
4082       const SMDS_MeshNode* nodeInFace = 0;
4083       itN = elem->nodesIterator();
4084       nn = 0; nbn =  elem->NbNodes();
4085       if(elem->IsQuadratic())
4086         nbn = nbn/2;
4087       while ( nn++ < nbn ) {
4088         node = static_cast<const SMDS_MeshNode*>( itN->next() );
4089         if ( node->GetPosition()->GetDim() == 2 )
4090           nodeInFace = node;
4091         if ( uvMap.find( node ) == uvMap.end() )
4092           uvCheckNodes.push_back( node );
4093         // add nodes of elems sharing node
4094         //         SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
4095         //         while ( eIt->more() ) {
4096         //           const SMDS_MeshElement* e = eIt->next();
4097         //           if ( e != elem ) {
4098         //             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4099         //             while ( nIt->more() ) {
4100         //               const SMDS_MeshNode* n =
4101         //                 static_cast<const SMDS_MeshNode*>( nIt->next() );
4102         //               if ( uvMap.find( n ) == uvMap.end() )
4103         //                 uvCheckNodes.push_back( n );
4104         //             }
4105         //           }
4106         //         }
4107       }
4108       // check UV on face
4109       list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
4110       for ( ; n != uvCheckNodes.end(); ++n ) {
4111         node = *n;
4112         gp_XY uv( 0, 0 );
4113         const SMDS_PositionPtr& pos = node->GetPosition();
4114         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
4115         // get existing UV
4116         if ( pos )
4117         {
4118           bool toCheck = true;
4119           uv = helper.GetNodeUV( face, node, nodeInFace, &toCheck );
4120         }
4121         // compute not existing UV
4122         bool project = ( posType == SMDS_TOP_3DSPACE );
4123         // double dist1 = DBL_MAX, dist2 = 0;
4124         // if ( posType != SMDS_TOP_3DSPACE ) {
4125         //   dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
4126         //   project = dist1 > fToler2;
4127         // }
4128         if ( project ) { // compute new UV
4129           gp_XY newUV;
4130           gp_Pnt pNode = SMESH_TNodeXYZ( node );
4131           if ( !getClosestUV( projector, pNode, newUV )) {
4132             MESSAGE("Node Projection Failed " << node);
4133           }
4134           else {
4135             if ( isUPeriodic )
4136               newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
4137             if ( isVPeriodic )
4138               newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
4139             // check new UV
4140             // if ( posType != SMDS_TOP_3DSPACE )
4141             //   dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
4142             // if ( dist2 < dist1 )
4143               uv = newUV;
4144           }
4145         }
4146         // store UV in the map
4147         listUV.push_back( uv );
4148         uvMap.insert( make_pair( node, &listUV.back() ));
4149       }
4150     } // loop on not yet smoothed elements
4151
4152     if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
4153       checkBoundaryNodes = true;
4154
4155     // fix nodes on mesh boundary
4156
4157     if ( checkBoundaryNodes ) {
4158       map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
4159       map< SMESH_TLink, int >::iterator link_nb;
4160       // put all elements links to linkNbMap
4161       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4162       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4163         const SMDS_MeshElement* elem = (*elemIt);
4164         int nbn =  elem->NbCornerNodes();
4165         // loop on elem links: insert them in linkNbMap
4166         for ( int iN = 0; iN < nbn; ++iN ) {
4167           const SMDS_MeshNode* n1 = elem->GetNode( iN );
4168           const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
4169           SMESH_TLink link( n1, n2 );
4170           link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
4171           link_nb->second++;
4172         }
4173       }
4174       // remove nodes that are in links encountered only once from setMovableNodes
4175       for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
4176         if ( link_nb->second == 1 ) {
4177           setMovableNodes.erase( link_nb->first.node1() );
4178           setMovableNodes.erase( link_nb->first.node2() );
4179         }
4180       }
4181     }
4182
4183     // -----------------------------------------------------
4184     // for nodes on seam edge, compute one more UV ( uvMap2 );
4185     // find movable nodes linked to nodes on seam and which
4186     // are to be smoothed using the second UV ( uvMap2 )
4187     // -----------------------------------------------------
4188
4189     set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
4190     if ( !surface.IsNull() ) {
4191       TopExp_Explorer eExp( face, TopAbs_EDGE );
4192       for ( ; eExp.More(); eExp.Next() ) {
4193         TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
4194         if ( !BRep_Tool::IsClosed( edge, face ))
4195           continue;
4196         SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
4197         if ( !sm ) continue;
4198         // find out which parameter varies for a node on seam
4199         double f,l;
4200         gp_Pnt2d uv1, uv2;
4201         Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
4202         if ( pcurve.IsNull() ) continue;
4203         uv1 = pcurve->Value( f );
4204         edge.Reverse();
4205         pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
4206         if ( pcurve.IsNull() ) continue;
4207         uv2 = pcurve->Value( f );
4208         int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
4209         // assure uv1 < uv2
4210         if ( uv1.Coord( iPar ) > uv2.Coord( iPar ))
4211           std::swap( uv1, uv2 );
4212         // get nodes on seam and its vertices
4213         list< const SMDS_MeshNode* > seamNodes;
4214         SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
4215         while ( nSeamIt->more() ) {
4216           const SMDS_MeshNode* node = nSeamIt->next();
4217           if ( !isQuadratic || !IsMedium( node ))
4218             seamNodes.push_back( node );
4219         }
4220         TopExp_Explorer vExp( edge, TopAbs_VERTEX );
4221         for ( ; vExp.More(); vExp.Next() ) {
4222           sm = aMesh->MeshElements( vExp.Current() );
4223           if ( sm ) {
4224             nSeamIt = sm->GetNodes();
4225             while ( nSeamIt->more() )
4226               seamNodes.push_back( nSeamIt->next() );
4227           }
4228         }
4229         // loop on nodes on seam
4230         list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
4231         for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
4232           const SMDS_MeshNode* nSeam = *noSeIt;
4233           map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
4234           if ( n_uv == uvMap.end() )
4235             continue;
4236           // set the first UV
4237           n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
4238           // set the second UV
4239           listUV.push_back( *n_uv->second );
4240           listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
4241           if ( uvMap2.empty() )
4242             uvMap2 = uvMap; // copy the uvMap contents
4243           uvMap2[ nSeam ] = &listUV.back();
4244
4245           // collect movable nodes linked to ones on seam in nodesNearSeam
4246           SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
4247           while ( eIt->more() ) {
4248             const SMDS_MeshElement* e = eIt->next();
4249             int nbUseMap1 = 0, nbUseMap2 = 0;
4250             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4251             int nn = 0, nbn =  e->NbNodes();
4252             if(e->IsQuadratic()) nbn = nbn/2;
4253             while ( nn++ < nbn )
4254             {
4255               const SMDS_MeshNode* n =
4256                 static_cast<const SMDS_MeshNode*>( nIt->next() );
4257               if (n == nSeam ||
4258                   setMovableNodes.find( n ) == setMovableNodes.end() )
4259                 continue;
4260               // add only nodes being closer to uv2 than to uv1
4261               // gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
4262               //              0.5 * ( n->Y() + nSeam->Y() ),
4263               //              0.5 * ( n->Z() + nSeam->Z() ));
4264               // gp_XY uv;
4265               // getClosestUV( projector, pMid, uv );
4266               double x = uvMap[ n ]->Coord( iPar );
4267               if ( Abs( uv1.Coord( iPar ) - x ) >
4268                    Abs( uv2.Coord( iPar ) - x )) {
4269                 nodesNearSeam.insert( n );
4270                 nbUseMap2++;
4271               }
4272               else
4273                 nbUseMap1++;
4274             }
4275             // for centroidalSmooth all element nodes must
4276             // be on one side of a seam
4277             if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
4278               SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4279               nn = 0;
4280               while ( nn++ < nbn ) {
4281                 const SMDS_MeshNode* n =
4282                   static_cast<const SMDS_MeshNode*>( nIt->next() );
4283                 setMovableNodes.erase( n );
4284               }
4285             }
4286           }
4287         } // loop on nodes on seam
4288       } // loop on edge of a face
4289     } // if ( !face.IsNull() )
4290
4291     if ( setMovableNodes.empty() ) {
4292       MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
4293       continue; // goto next face
4294     }
4295
4296     // -------------
4297     // SMOOTHING //
4298     // -------------
4299
4300     int it = -1;
4301     double maxRatio = -1., maxDisplacement = -1.;
4302     set<const SMDS_MeshNode*>::iterator nodeToMove;
4303     for ( it = 0; it < theNbIterations; it++ ) {
4304       maxDisplacement = 0.;
4305       nodeToMove = setMovableNodes.begin();
4306       for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4307         const SMDS_MeshNode* node = (*nodeToMove);
4308         gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
4309
4310         // smooth
4311         bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
4312         if ( theSmoothMethod == LAPLACIAN )
4313           laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
4314         else
4315           centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
4316
4317         // node displacement
4318         gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
4319         Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
4320         if ( aDispl > maxDisplacement )
4321           maxDisplacement = aDispl;
4322       }
4323       // no node movement => exit
4324       //if ( maxDisplacement < 1.e-16 ) {
4325       if ( maxDisplacement < disttol ) {
4326         MESSAGE("-- no node movement --");
4327         break;
4328       }
4329
4330       // check elements quality
4331       maxRatio  = 0;
4332       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4333       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4334         const SMDS_MeshElement* elem = (*elemIt);
4335         if ( !elem || elem->GetType() != SMDSAbs_Face )
4336           continue;
4337         SMESH::Controls::TSequenceOfXYZ aPoints;
4338         if ( aQualityFunc.GetPoints( elem, aPoints )) {
4339           double aValue = aQualityFunc.GetValue( aPoints );
4340           if ( aValue > maxRatio )
4341             maxRatio = aValue;
4342         }
4343       }
4344       if ( maxRatio <= theTgtAspectRatio ) {
4345         MESSAGE("-- quality achived --");
4346         break;
4347       }
4348       if (it+1 == theNbIterations) {
4349         MESSAGE("-- Iteration limit exceeded --");
4350       }
4351     } // smoothing iterations
4352
4353     MESSAGE(" Face id: " << *fId <<
4354             " Nb iterstions: " << it <<
4355             " Displacement: " << maxDisplacement <<
4356             " Aspect Ratio " << maxRatio);
4357
4358     // ---------------------------------------
4359     // new nodes positions are computed,
4360     // record movement in DS and set new UV
4361     // ---------------------------------------
4362     nodeToMove = setMovableNodes.begin();
4363     for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4364       SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
4365       aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
4366       map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
4367       if ( node_uv != uvMap.end() ) {
4368         gp_XY* uv = node_uv->second;
4369         node->SetPosition
4370           ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
4371       }
4372     }
4373
4374     // move medium nodes of quadratic elements
4375     if ( isQuadratic )
4376     {
4377       vector<const SMDS_MeshNode*> nodes;
4378       bool checkUV;
4379       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4380       for ( ; elemIt != elemsOnFace.end(); ++elemIt )
4381       {
4382         const SMDS_MeshElement* QF = *elemIt;
4383         if ( QF->IsQuadratic() )
4384         {
4385           nodes.assign( SMDS_MeshElement::iterator( QF->interlacedNodesElemIterator() ),
4386                         SMDS_MeshElement::iterator() );
4387           nodes.push_back( nodes[0] );
4388           gp_Pnt xyz;
4389           for (size_t i = 1; i < nodes.size(); i += 2 ) // i points to a medium node
4390           {
4391             if ( !surface.IsNull() )
4392             {
4393               gp_XY uv1 = helper.GetNodeUV( face, nodes[i-1], nodes[i+1], &checkUV );
4394               gp_XY uv2 = helper.GetNodeUV( face, nodes[i+1], nodes[i-1], &checkUV );
4395               gp_XY uv  = helper.GetMiddleUV( surface, uv1, uv2 );
4396               xyz = surface->Value( uv.X(), uv.Y() );
4397             }
4398             else {
4399               xyz = 0.5 * ( SMESH_TNodeXYZ( nodes[i-1] ) + SMESH_TNodeXYZ( nodes[i+1] ));
4400             }
4401             if (( SMESH_TNodeXYZ( nodes[i] ) - xyz.XYZ() ).Modulus() > disttol )
4402               // we have to move a medium node
4403               aMesh->MoveNode( nodes[i], xyz.X(), xyz.Y(), xyz.Z() );
4404           }
4405         }
4406       }
4407     }
4408
4409   } // loop on face ids
4410
4411 }
4412
4413 namespace
4414 {
4415   //=======================================================================
4416   //function : isReverse
4417   //purpose  : Return true if normal of prevNodes is not co-directied with
4418   //           gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
4419   //           iNotSame is where prevNodes and nextNodes are different.
4420   //           If result is true then future volume orientation is OK
4421   //=======================================================================
4422
4423   bool isReverse(const SMDS_MeshElement*             face,
4424                  const vector<const SMDS_MeshNode*>& prevNodes,
4425                  const vector<const SMDS_MeshNode*>& nextNodes,
4426                  const int                           iNotSame)
4427   {
4428
4429     SMESH_TNodeXYZ pP = prevNodes[ iNotSame ];
4430     SMESH_TNodeXYZ pN = nextNodes[ iNotSame ];
4431     gp_XYZ extrDir( pN - pP ), faceNorm;
4432     SMESH_MeshAlgos::FaceNormal( face, faceNorm, /*normalized=*/false );
4433
4434     return faceNorm * extrDir < 0.0;
4435   }
4436
4437   //================================================================================
4438   /*!
4439    * \brief Assure that theElemSets[0] holds elements, not nodes
4440    */
4441   //================================================================================
4442
4443   void setElemsFirst( TIDSortedElemSet theElemSets[2] )
4444   {
4445     if ( !theElemSets[0].empty() &&
4446          (*theElemSets[0].begin())->GetType() == SMDSAbs_Node )
4447     {
4448       std::swap( theElemSets[0], theElemSets[1] );
4449     }
4450     else if ( !theElemSets[1].empty() &&
4451               (*theElemSets[1].begin())->GetType() != SMDSAbs_Node )
4452     {
4453       std::swap( theElemSets[0], theElemSets[1] );
4454     }
4455   }
4456 }
4457
4458 //=======================================================================
4459 /*!
4460  * \brief Create elements by sweeping an element
4461  * \param elem - element to sweep
4462  * \param newNodesItVec - nodes generated from each node of the element
4463  * \param newElems - generated elements
4464  * \param nbSteps - number of sweeping steps
4465  * \param srcElements - to append elem for each generated element
4466  */
4467 //=======================================================================
4468
4469 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement*               elem,
4470                                     const vector<TNodeOfNodeListMapItr> & newNodesItVec,
4471                                     list<const SMDS_MeshElement*>&        newElems,
4472                                     const size_t                          nbSteps,
4473                                     SMESH_SequenceOfElemPtr&              srcElements)
4474 {
4475   SMESHDS_Mesh* aMesh = GetMeshDS();
4476
4477   const int           nbNodes = elem->NbNodes();
4478   const int         nbCorners = elem->NbCornerNodes();
4479   SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
4480                                                           polyhedron creation !!! */
4481   // Loop on elem nodes:
4482   // find new nodes and detect same nodes indices
4483   vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
4484   vector<const SMDS_MeshNode*> prevNod( nbNodes );
4485   vector<const SMDS_MeshNode*> nextNod( nbNodes );
4486   vector<const SMDS_MeshNode*> midlNod( nbNodes );
4487
4488   int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
4489   vector<int> sames(nbNodes);
4490   vector<bool> isSingleNode(nbNodes);
4491
4492   for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4493     TNodeOfNodeListMapItr                        nnIt = newNodesItVec[ iNode ];
4494     const SMDS_MeshNode*                         node = nnIt->first;
4495     const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
4496     if ( listNewNodes.empty() )
4497       return;
4498
4499     itNN   [ iNode ] = listNewNodes.begin();
4500     prevNod[ iNode ] = node;
4501     nextNod[ iNode ] = listNewNodes.front();
4502
4503     isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
4504                                                              corner node of linear */
4505     if ( prevNod[ iNode ] != nextNod [ iNode ])
4506       nbDouble += !isSingleNode[iNode];
4507
4508     if( iNode < nbCorners ) { // check corners only
4509       if ( prevNod[ iNode ] == nextNod [ iNode ])
4510         sames[nbSame++] = iNode;
4511       else
4512         iNotSameNode = iNode;
4513     }
4514   }
4515
4516   if ( nbSame == nbNodes || nbSame > 2) {
4517     MESSAGE( " Too many same nodes of element " << elem->GetID() );
4518     return;
4519   }
4520
4521   if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
4522   {
4523     // fix nodes order to have bottom normal external
4524     if ( baseType == SMDSEntity_Polygon )
4525     {
4526       std::reverse( itNN.begin(), itNN.end() );
4527       std::reverse( prevNod.begin(), prevNod.end() );
4528       std::reverse( midlNod.begin(), midlNod.end() );
4529       std::reverse( nextNod.begin(), nextNod.end() );
4530       std::reverse( isSingleNode.begin(), isSingleNode.end() );
4531     }
4532     else
4533     {
4534       const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType, nbNodes );
4535       SMDS_MeshCell::applyInterlace( ind, itNN );
4536       SMDS_MeshCell::applyInterlace( ind, prevNod );
4537       SMDS_MeshCell::applyInterlace( ind, nextNod );
4538       SMDS_MeshCell::applyInterlace( ind, midlNod );
4539       SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4540       if ( nbSame > 0 )
4541       {
4542         sames[nbSame] = iNotSameNode;
4543         for ( int j = 0; j <= nbSame; ++j )
4544           for ( size_t i = 0; i < ind.size(); ++i )
4545             if ( ind[i] == sames[j] )
4546             {
4547               sames[j] = i;
4548               break;
4549             }
4550         iNotSameNode = sames[nbSame];
4551       }
4552     }
4553   }
4554   else if ( elem->GetType() == SMDSAbs_Edge )
4555   {
4556     // orient a new face same as adjacent one
4557     int i1, i2;
4558     const SMDS_MeshElement* e;
4559     TIDSortedElemSet dummy;
4560     if (( e = SMESH_MeshAlgos::FindFaceInSet( nextNod[0], prevNod[0], dummy,dummy, &i1, &i2 )) ||
4561         ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[1], nextNod[1], dummy,dummy, &i1, &i2 )) ||
4562         ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[0], prevNod[1], dummy,dummy, &i1, &i2 )))
4563     {
4564       // there is an adjacent face, check order of nodes in it
4565       bool sameOrder = ( Abs( i2 - i1 ) == 1 ) ? ( i2 > i1 ) : ( i2 < i1 );
4566       if ( sameOrder )
4567       {
4568         std::swap( itNN[0],    itNN[1] );
4569         std::swap( prevNod[0], prevNod[1] );
4570         std::swap( nextNod[0], nextNod[1] );
4571         isSingleNode.swap( isSingleNode[0], isSingleNode[1] );
4572         if ( nbSame > 0 )
4573           sames[0] = 1 - sames[0];
4574         iNotSameNode = 1 - iNotSameNode;
4575       }
4576     }
4577   }
4578
4579   int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
4580   if ( nbSame > 0 ) {
4581     iSameNode    = sames[ nbSame-1 ];
4582     iBeforeSame  = ( iSameNode + nbCorners - 1 ) % nbCorners;
4583     iAfterSame   = ( iSameNode + 1 ) % nbCorners;
4584     iOpposSame   = ( iSameNode - 2 < 0  ? iSameNode + 2 : iSameNode - 2 );
4585   }
4586
4587   if ( baseType == SMDSEntity_Polygon )
4588   {
4589     if      ( nbNodes == 3 ) baseType = SMDSEntity_Triangle;
4590     else if ( nbNodes == 4 ) baseType = SMDSEntity_Quadrangle;
4591   }
4592   else if ( baseType == SMDSEntity_Quad_Polygon )
4593   {
4594     if      ( nbNodes == 6 ) baseType = SMDSEntity_Quad_Triangle;
4595     else if ( nbNodes == 8 ) baseType = SMDSEntity_Quad_Quadrangle;
4596   }
4597
4598   // make new elements
4599   for ( size_t iStep = 0; iStep < nbSteps; iStep++ )
4600   {
4601     // get next nodes
4602     for ( iNode = 0; iNode < nbNodes; iNode++ )
4603     {
4604       midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
4605       nextNod[ iNode ] = *itNN[ iNode ]++;
4606     }
4607
4608     SMDS_MeshElement* aNewElem = 0;
4609     /*if(!elem->IsPoly())*/ {
4610       switch ( baseType ) {
4611       case SMDSEntity_0D:
4612       case SMDSEntity_Node: { // sweep NODE
4613         if ( nbSame == 0 ) {
4614           if ( isSingleNode[0] )
4615             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
4616           else
4617             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
4618         }
4619         else
4620           return;
4621         break;
4622       }
4623       case SMDSEntity_Edge: { // sweep EDGE
4624         if ( nbDouble == 0 )
4625         {
4626           if ( nbSame == 0 ) // ---> quadrangle
4627             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4628                                       nextNod[ 1 ], nextNod[ 0 ] );
4629           else               // ---> triangle
4630             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4631                                       nextNod[ iNotSameNode ] );
4632         }
4633         else                 // ---> polygon
4634         {
4635           vector<const SMDS_MeshNode*> poly_nodes;
4636           poly_nodes.push_back( prevNod[0] );
4637           poly_nodes.push_back( prevNod[1] );
4638           if ( prevNod[1] != nextNod[1] )
4639           {
4640             if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
4641             poly_nodes.push_back( nextNod[1] );
4642           }
4643           if ( prevNod[0] != nextNod[0] )
4644           {
4645             poly_nodes.push_back( nextNod[0] );
4646             if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
4647           }
4648           switch ( poly_nodes.size() ) {
4649           case 3:
4650             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
4651             break;
4652           case 4:
4653             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
4654                                        poly_nodes[ 2 ], poly_nodes[ 3 ]);
4655             break;
4656           default:
4657             aNewElem = aMesh->AddPolygonalFace (poly_nodes);
4658           }
4659         }
4660         break;
4661       }
4662       case SMDSEntity_Triangle: // TRIANGLE --->
4663         {
4664           if ( nbDouble > 0 ) break;
4665           if ( nbSame == 0 )       // ---> pentahedron
4666             aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4667                                          nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
4668
4669           else if ( nbSame == 1 )  // ---> pyramid
4670             aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4671                                          nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
4672                                          nextNod[ iSameNode ]);
4673
4674           else // 2 same nodes:       ---> tetrahedron
4675             aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4676                                          nextNod[ iNotSameNode ]);
4677           break;
4678         }
4679       case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
4680         {
4681           if ( nbSame == 2 )
4682             return;
4683           if ( nbDouble+nbSame == 2 )
4684           {
4685             if(nbSame==0) {      // ---> quadratic quadrangle
4686               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4687                                         prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
4688             }
4689             else { //(nbSame==1) // ---> quadratic triangle
4690               if(sames[0]==2) {
4691                 return; // medium node on axis
4692               }
4693               else if(sames[0]==0)
4694                 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1],
4695                                           prevNod[2], midlNod[1], nextNod[2] );
4696               else // sames[0]==1
4697                 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[0],
4698                                           prevNod[2], nextNod[2], midlNod[0]);
4699             }
4700           }
4701           else if ( nbDouble == 3 )
4702           {
4703             if ( nbSame == 0 ) {  // ---> bi-quadratic quadrangle
4704               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4705                                         prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
4706             }
4707           }
4708           else
4709             return;
4710           break;
4711         }
4712       case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
4713         if ( nbDouble > 0 ) break;
4714
4715         if ( nbSame == 0 )       // ---> hexahedron
4716           aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
4717                                        nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
4718
4719         else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
4720           aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4721                                        nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
4722                                        nextNod[ iSameNode ]);
4723           newElems.push_back( aNewElem );
4724           aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ],  prevNod[ iOpposSame ],
4725                                        prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
4726                                        nextNod[ iOpposSame ],  nextNod[ iBeforeSame ] );
4727         }
4728         else if ( nbSame == 2 ) { // ---> pentahedron
4729           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
4730             // iBeforeSame is same too
4731             aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
4732                                          nextNod[ iOpposSame ],  prevNod[ iSameNode ],
4733                                          prevNod[ iAfterSame ],  nextNod[ iAfterSame ]);
4734           else
4735             // iAfterSame is same too
4736             aNewElem = aMesh->AddVolume (prevNod[ iSameNode ],   prevNod[ iBeforeSame ],
4737                                          nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
4738                                          prevNod[ iOpposSame ],  nextNod[ iOpposSame ]);
4739         }
4740         break;
4741       }
4742       case SMDSEntity_Quad_Triangle:  // sweep (Bi)Quadratic TRIANGLE --->
4743       case SMDSEntity_BiQuad_Triangle: /* ??? */ {
4744         if ( nbDouble+nbSame != 3 ) break;
4745         if(nbSame==0) {
4746           // --->  pentahedron with 15 nodes
4747           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4748                                        nextNod[0], nextNod[1], nextNod[2],
4749                                        prevNod[3], prevNod[4], prevNod[5],
4750                                        nextNod[3], nextNod[4], nextNod[5],
4751                                        midlNod[0], midlNod[1], midlNod[2]);
4752         }
4753         else if(nbSame==1) {
4754           // --->  2d order pyramid of 13 nodes
4755           int apex = iSameNode;
4756           int i0 = ( apex + 1 ) % nbCorners;
4757           int i1 = ( apex - 1 + nbCorners ) % nbCorners;
4758           int i0a = apex + 3;
4759           int i1a = i1 + 3;
4760           int i01 = i0 + 3;
4761           aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
4762                                       nextNod[i0], nextNod[i1], prevNod[apex],
4763                                       prevNod[i01], midlNod[i0],
4764                                       nextNod[i01], midlNod[i1],
4765                                       prevNod[i1a], prevNod[i0a],
4766                                       nextNod[i0a], nextNod[i1a]);
4767         }
4768         else if(nbSame==2) {
4769           // --->  2d order tetrahedron of 10 nodes
4770           int n1 = iNotSameNode;
4771           int n2 = ( n1 + 1             ) % nbCorners;
4772           int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
4773           int n12 = n1 + 3;
4774           int n23 = n2 + 3;
4775           int n31 = n3 + 3;
4776           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
4777                                        prevNod[n12], prevNod[n23], prevNod[n31],
4778                                        midlNod[n1], nextNod[n12], nextNod[n31]);
4779         }
4780         break;
4781       }
4782       case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
4783         if( nbSame == 0 ) {
4784           if ( nbDouble != 4 ) break;
4785           // --->  hexahedron with 20 nodes
4786           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4787                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4788                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4789                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4790                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
4791         }
4792         else if(nbSame==1) {
4793           // ---> pyramid + pentahedron - can not be created since it is needed
4794           // additional middle node at the center of face
4795           //INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
4796           return;
4797         }
4798         else if( nbSame == 2 ) {
4799           if ( nbDouble != 2 ) break;
4800           // --->  2d order Pentahedron with 15 nodes
4801           int n1,n2,n4,n5;
4802           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
4803             // iBeforeSame is same too
4804             n1 = iBeforeSame;
4805             n2 = iOpposSame;
4806             n4 = iSameNode;
4807             n5 = iAfterSame;
4808           }
4809           else {
4810             // iAfterSame is same too
4811             n1 = iSameNode;
4812             n2 = iBeforeSame;
4813             n4 = iAfterSame;
4814             n5 = iOpposSame;
4815           }
4816           int n12 = n2 + 4;
4817           int n45 = n4 + 4;
4818           int n14 = n1 + 4;
4819           int n25 = n5 + 4;
4820           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
4821                                        prevNod[n4], prevNod[n5], nextNod[n5],
4822                                        prevNod[n12], midlNod[n2], nextNod[n12],
4823                                        prevNod[n45], midlNod[n5], nextNod[n45],
4824                                        prevNod[n14], prevNod[n25], nextNod[n25]);
4825         }
4826         break;
4827       }
4828       case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
4829
4830         if( nbSame == 0 && nbDouble == 9 ) {
4831           // --->  tri-quadratic hexahedron with 27 nodes
4832           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4833                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4834                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4835                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4836                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4837                                        prevNod[8], // bottom center
4838                                        midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4839                                        nextNod[8], // top center
4840                                        midlNod[8]);// elem center
4841         }
4842         else
4843         {
4844           return;
4845         }
4846         break;
4847       }
4848       case SMDSEntity_Polygon: { // sweep POLYGON
4849
4850         if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4851           // --->  hexagonal prism
4852           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4853                                        prevNod[3], prevNod[4], prevNod[5],
4854                                        nextNod[0], nextNod[1], nextNod[2],
4855                                        nextNod[3], nextNod[4], nextNod[5]);
4856         }
4857         break;
4858       }
4859       case SMDSEntity_Ball:
4860         return;
4861
4862       default:
4863         break;
4864       } // switch ( baseType )
4865     } // scope
4866
4867     if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4868     {
4869       if ( baseType != SMDSEntity_Polygon )
4870       {
4871         const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType,nbNodes);
4872         SMDS_MeshCell::applyInterlace( ind, prevNod );
4873         SMDS_MeshCell::applyInterlace( ind, nextNod );
4874         SMDS_MeshCell::applyInterlace( ind, midlNod );
4875         SMDS_MeshCell::applyInterlace( ind, itNN );
4876         SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4877         baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4878       }
4879       vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4880       vector<int> quantities (nbNodes + 2);
4881       polyedre_nodes.clear();
4882       quantities.clear();
4883
4884       // bottom of prism
4885       for (int inode = 0; inode < nbNodes; inode++)
4886         polyedre_nodes.push_back( prevNod[inode] );
4887       quantities.push_back( nbNodes );
4888
4889       // top of prism
4890       polyedre_nodes.push_back( nextNod[0] );
4891       for (int inode = nbNodes; inode-1; --inode )
4892         polyedre_nodes.push_back( nextNod[inode-1] );
4893       quantities.push_back( nbNodes );
4894
4895       // side faces
4896       // 3--6--2
4897       // |     |
4898       // 7     5
4899       // |     |
4900       // 0--4--1
4901       const int iQuad = elem->IsQuadratic();
4902       for (int iface = 0; iface < nbNodes; iface += 1+iQuad )
4903       {
4904         const int prevNbNodes = polyedre_nodes.size(); // to detect degenerated face
4905         int inextface = (iface+1+iQuad) % nbNodes;
4906         int imid      = (iface+1) % nbNodes;
4907         polyedre_nodes.push_back( prevNod[inextface] );         // 0
4908         if ( iQuad ) polyedre_nodes.push_back( prevNod[imid] ); // 4
4909         polyedre_nodes.push_back( prevNod[iface] );             // 1
4910         if ( prevNod[iface] != nextNod[iface] ) // 1 != 2
4911         {
4912           if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]); // 5
4913           polyedre_nodes.push_back( nextNod[iface] );                         // 2
4914         }
4915         if ( iQuad ) polyedre_nodes.push_back( nextNod[imid] );               // 6
4916         if ( prevNod[inextface] != nextNod[inextface] ) // 0 != 3
4917         {
4918           polyedre_nodes.push_back( nextNod[inextface] );                            // 3
4919           if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);// 7
4920         }
4921         const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4922         if ( nbFaceNodes > 2 )
4923           quantities.push_back( nbFaceNodes );
4924         else // degenerated face
4925           polyedre_nodes.resize( prevNbNodes );
4926       }
4927       aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4928
4929     } // try to create a polyherdal prism
4930
4931     if ( aNewElem ) {
4932       newElems.push_back( aNewElem );
4933       myLastCreatedElems.Append(aNewElem);
4934       srcElements.Append( elem );
4935     }
4936
4937     // set new prev nodes
4938     for ( iNode = 0; iNode < nbNodes; iNode++ )
4939       prevNod[ iNode ] = nextNod[ iNode ];
4940
4941   } // loop on steps
4942 }
4943
4944 //=======================================================================
4945 /*!
4946  * \brief Create 1D and 2D elements around swept elements
4947  * \param mapNewNodes - source nodes and ones generated from them
4948  * \param newElemsMap - source elements and ones generated from them
4949  * \param elemNewNodesMap - nodes generated from each node of each element
4950  * \param elemSet - all swept elements
4951  * \param nbSteps - number of sweeping steps
4952  * \param srcElements - to append elem for each generated element
4953  */
4954 //=======================================================================
4955
4956 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap &     mapNewNodes,
4957                                   TTElemOfElemListMap &    newElemsMap,
4958                                   TElemOfVecOfNnlmiMap &   elemNewNodesMap,
4959                                   TIDSortedElemSet&        elemSet,
4960                                   const int                nbSteps,
4961                                   SMESH_SequenceOfElemPtr& srcElements)
4962 {
4963   ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4964   SMESHDS_Mesh* aMesh = GetMeshDS();
4965
4966   // Find nodes belonging to only one initial element - sweep them into edges.
4967
4968   TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4969   for ( ; nList != mapNewNodes.end(); nList++ )
4970   {
4971     const SMDS_MeshNode* node =
4972       static_cast<const SMDS_MeshNode*>( nList->first );
4973     if ( newElemsMap.count( node ))
4974       continue; // node was extruded into edge
4975     SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4976     int nbInitElems = 0;
4977     const SMDS_MeshElement* el = 0;
4978     SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4979     while ( eIt->more() && nbInitElems < 2 ) {
4980       const SMDS_MeshElement* e = eIt->next();
4981       SMDSAbs_ElementType  type = e->GetType();
4982       if ( type == SMDSAbs_Volume ||
4983            type < highType ||
4984            !elemSet.count(e))
4985         continue;
4986       if ( type > highType ) {
4987         nbInitElems = 0;
4988         highType    = type;
4989       }
4990       el = e;
4991       ++nbInitElems;
4992     }
4993     if ( nbInitElems == 1 ) {
4994       bool NotCreateEdge = el && el->IsMediumNode(node);
4995       if(!NotCreateEdge) {
4996         vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
4997         list<const SMDS_MeshElement*> newEdges;
4998         sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
4999       }
5000     }
5001   }
5002
5003   // Make a ceiling for each element ie an equal element of last new nodes.
5004   // Find free links of faces - make edges and sweep them into faces.
5005
5006   ElemFeatures polyFace( SMDSAbs_Face, /*isPoly=*/true ), anyFace;
5007
5008   TTElemOfElemListMap::iterator  itElem      = newElemsMap.begin();
5009   TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
5010   for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
5011   {
5012     const SMDS_MeshElement* elem = itElem->first;
5013     vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
5014
5015     if(itElem->second.size()==0) continue;
5016
5017     const bool isQuadratic = elem->IsQuadratic();
5018
5019     if ( elem->GetType() == SMDSAbs_Edge ) {
5020       // create a ceiling edge
5021       if ( !isQuadratic ) {
5022         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
5023                                vecNewNodes[ 1 ]->second.back())) {
5024           myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
5025                                                    vecNewNodes[ 1 ]->second.back()));
5026           srcElements.Append( elem );
5027         }
5028       }
5029       else {
5030         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
5031                                vecNewNodes[ 1 ]->second.back(),
5032                                vecNewNodes[ 2 ]->second.back())) {
5033           myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
5034                                                    vecNewNodes[ 1 ]->second.back(),
5035                                                    vecNewNodes[ 2 ]->second.back()));
5036           srcElements.Append( elem );
5037         }
5038       }
5039     }
5040     if ( elem->GetType() != SMDSAbs_Face )
5041       continue;
5042
5043     bool hasFreeLinks = false;
5044
5045     TIDSortedElemSet avoidSet;
5046     avoidSet.insert( elem );
5047
5048     set<const SMDS_MeshNode*> aFaceLastNodes;
5049     int iNode, nbNodes = vecNewNodes.size();
5050     if ( !isQuadratic ) {
5051       // loop on the face nodes
5052       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
5053         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5054         // look for free links of the face
5055         int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
5056         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
5057         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
5058         // check if a link n1-n2 is free
5059         if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
5060           hasFreeLinks = true;
5061           // make a new edge and a ceiling for a new edge
5062           const SMDS_MeshElement* edge;
5063           if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
5064             myLastCreatedElems.Append( edge = aMesh->AddEdge( n1, n2 )); // free link edge
5065             srcElements.Append( myLastCreatedElems.Last() );
5066           }
5067           n1 = vecNewNodes[ iNode ]->second.back();
5068           n2 = vecNewNodes[ iNext ]->second.back();
5069           if ( !aMesh->FindEdge( n1, n2 )) {
5070             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // new edge ceiling
5071             srcElements.Append( edge );
5072           }
5073         }
5074       }
5075     }
5076     else { // elem is quadratic face
5077       int nbn = nbNodes/2;
5078       for ( iNode = 0; iNode < nbn; iNode++ ) {
5079         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5080         int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
5081         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
5082         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
5083         const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
5084         // check if a link is free
5085         if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
5086              ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
5087              ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
5088           hasFreeLinks = true;
5089           // make an edge and a ceiling for a new edge
5090           // find medium node
5091           if ( !aMesh->FindEdge( n1, n2, n3 )) {
5092             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // free link edge
5093             srcElements.Append( elem );
5094           }
5095           n1 = vecNewNodes[ iNode ]->second.back();
5096           n2 = vecNewNodes[ iNext ]->second.back();
5097           n3 = vecNewNodes[ iNode+nbn ]->second.back();
5098           if ( !aMesh->FindEdge( n1, n2, n3 )) {
5099             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
5100             srcElements.Append( elem );
5101           }
5102         }
5103       }
5104       for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
5105         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5106       }
5107     }
5108
5109     // sweep free links into faces
5110
5111     if ( hasFreeLinks ) {
5112       list<const SMDS_MeshElement*> & newVolumes = itElem->second;
5113       int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
5114
5115       set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
5116       set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
5117       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
5118         initNodeSet.insert( vecNewNodes[ iNode ]->first );
5119         topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
5120       }
5121       if ( isQuadratic && nbNodes % 2 ) {  // node set for the case of a biquadratic
5122         initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
5123         initNodeSetNoCenter.erase( vecNewNodes.back()->first );
5124       }
5125       for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
5126         list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
5127         std::advance( v, volNb );
5128         // find indices of free faces of a volume and their source edges
5129         list< int > freeInd;
5130         list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
5131         SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
5132         int iF, nbF = vTool.NbFaces();
5133         for ( iF = 0; iF < nbF; iF ++ ) {
5134           if (vTool.IsFreeFace( iF ) &&
5135               vTool.GetFaceNodes( iF, faceNodeSet ) &&
5136               initNodeSet != faceNodeSet) // except an initial face
5137           {
5138             if ( nbSteps == 1 && faceNodeSet == topNodeSet )
5139               continue;
5140             if ( faceNodeSet == initNodeSetNoCenter )
5141               continue;
5142             freeInd.push_back( iF );
5143             // find source edge of a free face iF
5144             vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
5145             vector<const SMDS_MeshNode*>::iterator lastCommom;
5146             commonNodes.resize( nbNodes, 0 );
5147             lastCommom = std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
5148                                                 initNodeSet.begin(), initNodeSet.end(),
5149                                                 commonNodes.begin());
5150             if ( std::distance( commonNodes.begin(), lastCommom ) == 3 )
5151               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
5152             else
5153               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
5154 #ifdef _DEBUG_
5155             if ( !srcEdges.back() )
5156             {
5157               cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
5158                    << iF << " of volume #" << vTool.ID() << endl;
5159             }
5160 #endif
5161           }
5162         }
5163         if ( freeInd.empty() )
5164           continue;
5165
5166         // create wall faces for all steps;
5167         // if such a face has been already created by sweep of edge,
5168         // assure that its orientation is OK
5169         for ( int iStep = 0; iStep < nbSteps; iStep++ )
5170         {
5171           vTool.Set( *v, /*ignoreCentralNodes=*/false );
5172           vTool.SetExternalNormal();
5173           const int nextShift = vTool.IsForward() ? +1 : -1;
5174           list< int >::iterator ind = freeInd.begin();
5175           list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
5176           for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
5177           {
5178             const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
5179             int nbn = vTool.NbFaceNodes( *ind );
5180             const SMDS_MeshElement * f = 0;
5181             if ( nbn == 3 )              ///// triangle
5182             {
5183               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
5184               if ( !f ||
5185                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
5186               {
5187                 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
5188                                                      nodes[ 1 ],
5189                                                      nodes[ 1 + nextShift ] };
5190                 if ( f )
5191                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5192                 else
5193                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
5194                                                             newOrder[ 2 ] ));
5195               }
5196             }
5197             else if ( nbn == 4 )       ///// quadrangle
5198             {
5199               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
5200               if ( !f ||
5201                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
5202               {
5203                 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
5204                                                      nodes[ 2 ], nodes[ 2+nextShift ] };
5205                 if ( f )
5206                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5207                 else
5208                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
5209                                                             newOrder[ 2 ], newOrder[ 3 ]));
5210               }
5211             }
5212             else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
5213             {
5214               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
5215               if ( !f ||
5216                    nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
5217               {
5218                 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
5219                                                      nodes[2],
5220                                                      nodes[2 + 2*nextShift],
5221                                                      nodes[3 - 2*nextShift],
5222                                                      nodes[3],
5223                                                      nodes[3 + 2*nextShift]};
5224                 if ( f )
5225                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5226                 else
5227                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ],
5228                                                             newOrder[ 1 ],
5229                                                             newOrder[ 2 ],
5230                                                             newOrder[ 3 ],
5231                                                             newOrder[ 4 ],
5232                                                             newOrder[ 5 ] ));
5233               }
5234             }
5235             else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
5236             {
5237               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
5238                                    nodes[1], nodes[3], nodes[5], nodes[7] );
5239               if ( !f ||
5240                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5241               {
5242                 const SMDS_MeshNode* newOrder[8] = { nodes[0],
5243                                                      nodes[4 - 2*nextShift],
5244                                                      nodes[4],
5245                                                      nodes[4 + 2*nextShift],
5246                                                      nodes[1],
5247                                                      nodes[5 - 2*nextShift],
5248                                                      nodes[5],
5249                                                      nodes[5 + 2*nextShift] };
5250                 if ( f )
5251                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5252                 else
5253                   myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5254                                                            newOrder[ 2 ], newOrder[ 3 ],
5255                                                            newOrder[ 4 ], newOrder[ 5 ],
5256                                                            newOrder[ 6 ], newOrder[ 7 ]));
5257               }
5258             }
5259             else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
5260             {
5261               f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
5262                                       SMDSAbs_Face, /*noMedium=*/false);
5263               if ( !f ||
5264                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5265               {
5266                 const SMDS_MeshNode* newOrder[9] = { nodes[0],
5267                                                      nodes[4 - 2*nextShift],
5268                                                      nodes[4],
5269                                                      nodes[4 + 2*nextShift],
5270                                                      nodes[1],
5271                                                      nodes[5 - 2*nextShift],
5272                                                      nodes[5],
5273                                                      nodes[5 + 2*nextShift],
5274                                                      nodes[8] };
5275                 if ( f )
5276                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5277                 else
5278                   myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5279                                                            newOrder[ 2 ], newOrder[ 3 ],
5280                                                            newOrder[ 4 ], newOrder[ 5 ],
5281                                                            newOrder[ 6 ], newOrder[ 7 ],
5282                                                            newOrder[ 8 ]));
5283               }
5284             }
5285             else  //////// polygon
5286             {
5287               vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
5288               const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
5289               if ( !f ||
5290                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
5291               {
5292                 if ( !vTool.IsForward() )
5293                   std::reverse( polygon_nodes.begin(), polygon_nodes.end());
5294                 if ( f )
5295                   aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
5296                 else
5297                   AddElement( polygon_nodes, polyFace.SetQuad( (*v)->IsQuadratic() ));
5298               }
5299             }
5300
5301             while ( srcElements.Length() < myLastCreatedElems.Length() )
5302               srcElements.Append( *srcEdge );
5303
5304           }  // loop on free faces
5305
5306           // go to the next volume
5307           iVol = 0;
5308           while ( iVol++ < nbVolumesByStep ) v++;
5309
5310         } // loop on steps
5311       } // loop on volumes of one step
5312     } // sweep free links into faces
5313
5314     // Make a ceiling face with a normal external to a volume
5315
5316     // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
5317     SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
5318     int iF = lastVol.GetFaceIndex( aFaceLastNodes );
5319
5320     if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
5321       aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
5322       iF = lastVol.GetFaceIndex( aFaceLastNodes );
5323     }
5324     if ( iF >= 0 )
5325     {
5326       lastVol.SetExternalNormal();
5327       const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
5328       const               int nbn = lastVol.NbFaceNodes( iF );
5329       vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
5330       if ( !hasFreeLinks ||
5331            !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
5332       {
5333         const vector<int>& interlace =
5334           SMDS_MeshCell::interlacedSmdsOrder( elem->GetEntityType(), nbn );
5335         SMDS_MeshCell::applyInterlaceRev( interlace, nodeVec );
5336
5337         AddElement( nodeVec, anyFace.Init( elem ));
5338
5339         while ( srcElements.Length() < myLastCreatedElems.Length() )
5340           srcElements.Append( elem );
5341       }
5342     }
5343   } // loop on swept elements
5344 }
5345
5346 //=======================================================================
5347 //function : RotationSweep
5348 //purpose  :
5349 //=======================================================================
5350
5351 SMESH_MeshEditor::PGroupIDs
5352 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet   theElemSets[2],
5353                                 const gp_Ax1&      theAxis,
5354                                 const double       theAngle,
5355                                 const int          theNbSteps,
5356                                 const double       theTol,
5357                                 const bool         theMakeGroups,
5358                                 const bool         theMakeWalls)
5359 {
5360   myLastCreatedElems.Clear();
5361   myLastCreatedNodes.Clear();
5362
5363   // source elements for each generated one
5364   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5365
5366   gp_Trsf aTrsf;
5367   aTrsf.SetRotation( theAxis, theAngle );
5368   gp_Trsf aTrsf2;
5369   aTrsf2.SetRotation( theAxis, theAngle/2. );
5370
5371   gp_Lin aLine( theAxis );
5372   double aSqTol = theTol * theTol;
5373
5374   SMESHDS_Mesh* aMesh = GetMeshDS();
5375
5376   TNodeOfNodeListMap mapNewNodes;
5377   TElemOfVecOfNnlmiMap mapElemNewNodes;
5378   TTElemOfElemListMap newElemsMap;
5379
5380   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5381                                      myMesh->NbFaces(ORDER_QUADRATIC) +
5382                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
5383   // loop on theElemSets
5384   setElemsFirst( theElemSets );
5385   TIDSortedElemSet::iterator itElem;
5386   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5387   {
5388     TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5389     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5390       const SMDS_MeshElement* elem = *itElem;
5391       if ( !elem || elem->GetType() == SMDSAbs_Volume )
5392         continue;
5393       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5394       newNodesItVec.reserve( elem->NbNodes() );
5395
5396       // loop on elem nodes
5397       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5398       while ( itN->more() )
5399       {
5400         const SMDS_MeshNode* node = cast2Node( itN->next() );
5401
5402         gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
5403         double coord[3];
5404         aXYZ.Coord( coord[0], coord[1], coord[2] );
5405         bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
5406
5407         // check if a node has been already sweeped
5408         TNodeOfNodeListMapItr nIt =
5409           mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5410         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5411         if ( listNewNodes.empty() )
5412         {
5413           // check if we are to create medium nodes between corner ones
5414           bool needMediumNodes = false;
5415           if ( isQuadraticMesh )
5416           {
5417             SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5418             while (it->more() && !needMediumNodes )
5419             {
5420               const SMDS_MeshElement* invElem = it->next();
5421               if ( invElem != elem && !theElems.count( invElem )) continue;
5422               needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5423               if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5424                 needMediumNodes = true;
5425             }
5426           }
5427
5428           // make new nodes
5429           const SMDS_MeshNode * newNode = node;
5430           for ( int i = 0; i < theNbSteps; i++ ) {
5431             if ( !isOnAxis ) {
5432               if ( needMediumNodes )  // create a medium node
5433               {
5434                 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5435                 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5436                 myLastCreatedNodes.Append(newNode);
5437                 srcNodes.Append( node );
5438                 listNewNodes.push_back( newNode );
5439                 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5440               }
5441               else {
5442                 aTrsf.Transforms( coord[0], coord[1], coord[2] );
5443               }
5444               // create a corner node
5445               newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5446               myLastCreatedNodes.Append(newNode);
5447               srcNodes.Append( node );
5448               listNewNodes.push_back( newNode );
5449             }
5450             else {
5451               listNewNodes.push_back( newNode );
5452               // if ( needMediumNodes )
5453               //   listNewNodes.push_back( newNode );
5454             }
5455           }
5456         }
5457         newNodesItVec.push_back( nIt );
5458       }
5459       // make new elements
5460       sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
5461     }
5462   }
5463
5464   if ( theMakeWalls )
5465     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], theNbSteps, srcElems );
5466
5467   PGroupIDs newGroupIDs;
5468   if ( theMakeGroups )
5469     newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
5470
5471   return newGroupIDs;
5472 }
5473
5474 //=======================================================================
5475 //function : ExtrusParam
5476 //purpose  : standard construction
5477 //=======================================================================
5478
5479 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Vec&            theStep,
5480                                             const int                theNbSteps,
5481                                             const std::list<double>& theScales,
5482                                             const gp_XYZ*            theBasePoint,
5483                                             const int                theFlags,
5484                                             const double             theTolerance):
5485   myDir( theStep ),
5486   myBaseP( Precision::Infinite(), 0, 0 ),
5487   myFlags( theFlags ),
5488   myTolerance( theTolerance ),
5489   myElemsToUse( NULL )
5490 {
5491   mySteps = new TColStd_HSequenceOfReal;
5492   const double stepSize = theStep.Magnitude();
5493   for (int i=1; i<=theNbSteps; i++ )
5494     mySteps->Append( stepSize );
5495
5496   int nbScales = theScales.size();
5497   if ( nbScales > 0 )
5498   {
5499     if ( IsLinearVariation() && nbScales < theNbSteps )
5500     {
5501       myScales.reserve( theNbSteps );
5502       std::list<double>::const_iterator scale = theScales.begin();
5503       double prevScale = 1.0;
5504       for ( int iSc = 1; scale != theScales.end(); ++scale, ++iSc )
5505       {
5506         int      iStep = int( iSc / double( nbScales ) * theNbSteps + 0.5 );
5507         int    stDelta = Max( 1, iStep - myScales.size());
5508         double scDelta = ( *scale - prevScale ) / stDelta;
5509         for ( int iStep = 0; iStep < stDelta; ++iStep )
5510         {
5511           myScales.push_back( prevScale + scDelta );
5512           prevScale = myScales.back();
5513         }
5514         prevScale = *scale;
5515       }
5516     }
5517     else
5518     {
5519       myScales.assign( theScales.begin(), theScales.end() );
5520     }
5521   }
5522   if ( theBasePoint )
5523   {
5524     myBaseP = *theBasePoint;
5525   }
5526
5527   if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5528       ( theTolerance > 0 ))
5529   {
5530     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5531   }
5532   else
5533   {
5534     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5535   }
5536 }
5537
5538 //=======================================================================
5539 //function : ExtrusParam
5540 //purpose  : steps are given explicitly
5541 //=======================================================================
5542
5543 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Dir&                   theDir,
5544                                             Handle(TColStd_HSequenceOfReal) theSteps,
5545                                             const int                       theFlags,
5546                                             const double                    theTolerance):
5547   myDir( theDir ),
5548   mySteps( theSteps ),
5549   myFlags( theFlags ),
5550   myTolerance( theTolerance ),
5551   myElemsToUse( NULL )
5552 {
5553   if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5554       ( theTolerance > 0 ))
5555   {
5556     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5557   }
5558   else
5559   {
5560     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5561   }
5562 }
5563
5564 //=======================================================================
5565 //function : ExtrusParam
5566 //purpose  : for extrusion by normal
5567 //=======================================================================
5568
5569 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const double theStepSize,
5570                                             const int    theNbSteps,
5571                                             const int    theFlags,
5572                                             const int    theDim ):
5573   myDir( 1,0,0 ),
5574   mySteps( new TColStd_HSequenceOfReal ),
5575   myFlags( theFlags ),
5576   myTolerance( 0 ),
5577   myElemsToUse( NULL )
5578 {
5579   for (int i = 0; i < theNbSteps; i++ )
5580     mySteps->Append( theStepSize );
5581
5582   if ( theDim == 1 )
5583   {
5584     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal1D;
5585   }
5586   else
5587   {
5588     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal2D;
5589   }
5590 }
5591
5592 //=======================================================================
5593 //function : ExtrusParam::SetElementsToUse
5594 //purpose  : stores elements to use for extrusion by normal, depending on
5595 //           state of EXTRUSION_FLAG_USE_INPUT_ELEMS_ONLY flag;
5596 //           define myBaseP for scaling
5597 //=======================================================================
5598
5599 void SMESH_MeshEditor::ExtrusParam::SetElementsToUse( const TIDSortedElemSet& elems,
5600                                                       const TIDSortedElemSet& nodes )
5601 {
5602   myElemsToUse = ToUseInpElemsOnly() ? & elems : 0;
5603
5604   if ( Precision::IsInfinite( myBaseP.X() )) // myBaseP not defined
5605   {
5606     myBaseP.SetCoord( 0.,0.,0. );
5607     TIDSortedElemSet newNodes;
5608
5609     const TIDSortedElemSet* elemSets[] = { &elems, &nodes };
5610     for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5611     {
5612       const TIDSortedElemSet& elements = *( elemSets[ is2ndSet ]);
5613       TIDSortedElemSet::const_iterator itElem = elements.begin();
5614       for ( ; itElem != elements.end(); itElem++ )
5615       {
5616         const SMDS_MeshElement* elem = *itElem;
5617         SMDS_ElemIteratorPtr     itN = elem->nodesIterator();
5618         while ( itN->more() ) {
5619           const SMDS_MeshElement* node = itN->next();
5620           if ( newNodes.insert( node ).second )
5621             myBaseP += SMESH_TNodeXYZ( node );
5622         }
5623       }
5624     }
5625     myBaseP /= newNodes.size();
5626   }
5627 }
5628
5629 //=======================================================================
5630 //function : ExtrusParam::beginStepIter
5631 //purpose  : prepare iteration on steps
5632 //=======================================================================
5633
5634 void SMESH_MeshEditor::ExtrusParam::beginStepIter( bool withMediumNodes )
5635 {
5636   myWithMediumNodes = withMediumNodes;
5637   myNextStep = 1;
5638   myCurSteps.clear();
5639 }
5640 //=======================================================================
5641 //function : ExtrusParam::moreSteps
5642 //purpose  : are there more steps?
5643 //=======================================================================
5644
5645 bool SMESH_MeshEditor::ExtrusParam::moreSteps()
5646 {
5647   return myNextStep <= mySteps->Length() || !myCurSteps.empty();
5648 }
5649 //=======================================================================
5650 //function : ExtrusParam::nextStep
5651 //purpose  : returns the next step
5652 //=======================================================================
5653
5654 double SMESH_MeshEditor::ExtrusParam::nextStep()
5655 {
5656   double res = 0;
5657   if ( !myCurSteps.empty() )
5658   {
5659     res = myCurSteps.back();
5660     myCurSteps.pop_back();
5661   }
5662   else if ( myNextStep <= mySteps->Length() )
5663   {
5664     myCurSteps.push_back( mySteps->Value( myNextStep ));
5665     ++myNextStep;
5666     if ( myWithMediumNodes )
5667     {
5668       myCurSteps.back() /= 2.;
5669       myCurSteps.push_back( myCurSteps.back() );
5670     }
5671     res = nextStep();
5672   }
5673   return res;
5674 }
5675
5676 //=======================================================================
5677 //function : ExtrusParam::makeNodesByDir
5678 //purpose  : create nodes for standard extrusion
5679 //=======================================================================
5680
5681 int SMESH_MeshEditor::ExtrusParam::
5682 makeNodesByDir( SMESHDS_Mesh*                     mesh,
5683                 const SMDS_MeshNode*              srcNode,
5684                 std::list<const SMDS_MeshNode*> & newNodes,
5685                 const bool                        makeMediumNodes)
5686 {
5687   gp_XYZ p = SMESH_TNodeXYZ( srcNode );
5688
5689   int nbNodes = 0;
5690   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5691   {
5692     p += myDir.XYZ() * nextStep();
5693     const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5694     newNodes.push_back( newNode );
5695   }
5696
5697   if ( !myScales.empty() )
5698   {
5699     if ( makeMediumNodes && myMediumScales.empty() )
5700     {
5701       myMediumScales.resize( myScales.size() );
5702       double prevFactor = 1.;
5703       for ( size_t i = 0; i < myScales.size(); ++i )
5704       {
5705         myMediumScales[i] = 0.5 * ( prevFactor + myScales[i] );
5706         prevFactor = myScales[i];
5707       }
5708     }
5709     typedef std::vector<double>::iterator ScaleIt;
5710     ScaleIt scales[] = { myScales.begin(), myMediumScales.begin() };
5711
5712     size_t iSc = 0, nbScales = myScales.size() + myMediumScales.size();
5713
5714     gp_XYZ center = myBaseP;
5715     std::list<const SMDS_MeshNode*>::iterator nIt = newNodes.begin();
5716     size_t iN  = 0;
5717     for ( beginStepIter( makeMediumNodes ); moreSteps() && ( iN < nbScales ); ++nIt, ++iN )
5718     {
5719       center += myDir.XYZ() * nextStep();
5720
5721       iSc += int( makeMediumNodes );
5722       ScaleIt& scale = scales[ iSc % 2 ];
5723       
5724       gp_XYZ xyz = SMESH_TNodeXYZ( *nIt );
5725       xyz = ( *scale * ( xyz - center )) + center;
5726       mesh->MoveNode( *nIt, xyz.X(), xyz.Y(), xyz.Z() );
5727
5728       ++scale;
5729     }
5730   }
5731   return nbNodes;
5732 }
5733
5734 //=======================================================================
5735 //function : ExtrusParam::makeNodesByDirAndSew
5736 //purpose  : create nodes for standard extrusion with sewing
5737 //=======================================================================
5738
5739 int SMESH_MeshEditor::ExtrusParam::
5740 makeNodesByDirAndSew( SMESHDS_Mesh*                     mesh,
5741                       const SMDS_MeshNode*              srcNode,
5742                       std::list<const SMDS_MeshNode*> & newNodes,
5743                       const bool                        makeMediumNodes)
5744 {
5745   gp_XYZ P1 = SMESH_TNodeXYZ( srcNode );
5746
5747   int nbNodes = 0;
5748   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5749   {
5750     P1 += myDir.XYZ() * nextStep();
5751
5752     // try to search in sequence of existing nodes
5753     // if myNodes.Length()>0 we 'nave to use given sequence
5754     // else - use all nodes of mesh
5755     const SMDS_MeshNode * node = 0;
5756     if ( myNodes.Length() > 0 ) {
5757       int i;
5758       for(i=1; i<=myNodes.Length(); i++) {
5759         gp_XYZ P2 = SMESH_TNodeXYZ( myNodes.Value(i) );
5760         if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5761         {
5762           node = myNodes.Value(i);
5763           break;
5764         }
5765       }
5766     }
5767     else {
5768       SMDS_NodeIteratorPtr itn = mesh->nodesIterator();
5769       while(itn->more()) {
5770         SMESH_TNodeXYZ P2( itn->next() );
5771         if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5772         {
5773           node = P2._node;
5774           break;
5775         }
5776       }
5777     }
5778
5779     if ( !node )
5780       node = mesh->AddNode( P1.X(), P1.Y(), P1.Z() );
5781
5782     newNodes.push_back( node );
5783
5784   } // loop on steps
5785
5786   return nbNodes;
5787 }
5788
5789 //=======================================================================
5790 //function : ExtrusParam::makeNodesByNormal2D
5791 //purpose  : create nodes for extrusion using normals of faces
5792 //=======================================================================
5793
5794 int SMESH_MeshEditor::ExtrusParam::
5795 makeNodesByNormal2D( SMESHDS_Mesh*                     mesh,
5796                      const SMDS_MeshNode*              srcNode,
5797                      std::list<const SMDS_MeshNode*> & newNodes,
5798                      const bool                        makeMediumNodes)
5799 {
5800   const bool alongAvgNorm = ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL );
5801
5802   gp_XYZ p = SMESH_TNodeXYZ( srcNode );
5803
5804   // get normals to faces sharing srcNode
5805   vector< gp_XYZ > norms, baryCenters;
5806   gp_XYZ norm, avgNorm( 0,0,0 );
5807   SMDS_ElemIteratorPtr faceIt = srcNode->GetInverseElementIterator( SMDSAbs_Face );
5808   while ( faceIt->more() )
5809   {
5810     const SMDS_MeshElement* face = faceIt->next();
5811     if ( myElemsToUse && !myElemsToUse->count( face ))
5812       continue;
5813     if ( SMESH_MeshAlgos::FaceNormal( face, norm, /*normalized=*/true ))
5814     {
5815       norms.push_back( norm );
5816       avgNorm += norm;
5817       if ( !alongAvgNorm )
5818       {
5819         gp_XYZ bc(0,0,0);
5820         int nbN = 0;
5821         for ( SMDS_ElemIteratorPtr nIt = face->nodesIterator(); nIt->more(); ++nbN )
5822           bc += SMESH_TNodeXYZ( nIt->next() );
5823         baryCenters.push_back( bc / nbN );
5824       }
5825     }
5826   }
5827
5828   if ( norms.empty() ) return 0;
5829
5830   double normSize = avgNorm.Modulus();
5831   if ( normSize < std::numeric_limits<double>::min() )
5832     return 0;
5833
5834   if ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL ) // extrude along avgNorm
5835   {
5836     myDir = avgNorm;
5837     return makeNodesByDir( mesh, srcNode, newNodes, makeMediumNodes );
5838   }
5839
5840   avgNorm /= normSize;
5841
5842   int nbNodes = 0;
5843   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5844   {
5845     gp_XYZ pNew = p;
5846     double stepSize = nextStep();
5847
5848     if ( norms.size() > 1 )
5849     {
5850       for ( size_t iF = 0; iF < norms.size(); ++iF ) // loop on faces
5851       {
5852         // translate plane of a face
5853         baryCenters[ iF ] += norms[ iF ] * stepSize;
5854
5855         // find point of intersection of the face plane located at baryCenters[ iF ]
5856         // and avgNorm located at pNew
5857         double d    = -( norms[ iF ] * baryCenters[ iF ]); // d of plane equation ax+by+cz+d=0
5858         double dot  = ( norms[ iF ] * avgNorm );
5859         if ( dot < std::numeric_limits<double>::min() )
5860           dot = stepSize * 1e-3;
5861         double step = -( norms[ iF ] * pNew + d ) / dot;
5862         pNew += step * avgNorm;
5863       }
5864     }
5865     else
5866     {
5867       pNew += stepSize * avgNorm;
5868     }
5869     p = pNew;
5870
5871     const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5872     newNodes.push_back( newNode );
5873   }
5874   return nbNodes;
5875 }
5876
5877 //=======================================================================
5878 //function : ExtrusParam::makeNodesByNormal1D
5879 //purpose  : create nodes for extrusion using normals of edges
5880 //=======================================================================
5881
5882 int SMESH_MeshEditor::ExtrusParam::
5883 makeNodesByNormal1D( SMESHDS_Mesh*                     mesh,
5884                      const SMDS_MeshNode*              srcNode,
5885                      std::list<const SMDS_MeshNode*> & newNodes,
5886                      const bool                        makeMediumNodes)
5887 {
5888   throw SALOME_Exception("Extrusion 1D by Normal not implemented");
5889   return 0;
5890 }
5891
5892 //=======================================================================
5893 //function : ExtrusionSweep
5894 //purpose  :
5895 //=======================================================================
5896
5897 SMESH_MeshEditor::PGroupIDs
5898 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet     theElems[2],
5899                                   const gp_Vec&        theStep,
5900                                   const int            theNbSteps,
5901                                   TTElemOfElemListMap& newElemsMap,
5902                                   const int            theFlags,
5903                                   const double         theTolerance)
5904 {
5905   ExtrusParam aParams( theStep, theNbSteps, std::list<double>(), 0, theFlags, theTolerance );
5906   return ExtrusionSweep( theElems, aParams, newElemsMap );
5907 }
5908
5909
5910 //=======================================================================
5911 //function : ExtrusionSweep
5912 //purpose  :
5913 //=======================================================================
5914
5915 SMESH_MeshEditor::PGroupIDs
5916 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet     theElemSets[2],
5917                                   ExtrusParam&         theParams,
5918                                   TTElemOfElemListMap& newElemsMap)
5919 {
5920   myLastCreatedElems.Clear();
5921   myLastCreatedNodes.Clear();
5922
5923   // source elements for each generated one
5924   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5925
5926   setElemsFirst( theElemSets );
5927   const int nbSteps = theParams.NbSteps();
5928   theParams.SetElementsToUse( theElemSets[0], theElemSets[1] );
5929
5930   TNodeOfNodeListMap   mapNewNodes;
5931   TElemOfVecOfNnlmiMap mapElemNewNodes;
5932
5933   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5934                                      myMesh->NbFaces(ORDER_QUADRATIC) +
5935                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
5936   // loop on theElems
5937   TIDSortedElemSet::iterator itElem;
5938   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5939   {
5940     TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5941     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5942     {
5943       // check element type
5944       const SMDS_MeshElement* elem = *itElem;
5945       if ( !elem  || elem->GetType() == SMDSAbs_Volume )
5946         continue;
5947
5948       const size_t nbNodes = elem->NbNodes();
5949       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5950       newNodesItVec.reserve( nbNodes );
5951
5952       // loop on elem nodes
5953       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5954       while ( itN->more() )
5955       {
5956         // check if a node has been already sweeped
5957         const SMDS_MeshNode* node = cast2Node( itN->next() );
5958         TNodeOfNodeListMap::iterator nIt =
5959           mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5960         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5961         if ( listNewNodes.empty() )
5962         {
5963           // make new nodes
5964
5965           // check if we are to create medium nodes between corner ones
5966           bool needMediumNodes = false;
5967           if ( isQuadraticMesh )
5968           {
5969             SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5970             while (it->more() && !needMediumNodes )
5971             {
5972               const SMDS_MeshElement* invElem = it->next();
5973               if ( invElem != elem && !theElems.count( invElem )) continue;
5974               needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5975               if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5976                 needMediumNodes = true;
5977             }
5978           }
5979           // create nodes for all steps
5980           if ( theParams.MakeNodes( GetMeshDS(), node, listNewNodes, needMediumNodes ))
5981           {
5982             list<const SMDS_MeshNode*>::iterator newNodesIt = listNewNodes.begin();
5983             for ( ; newNodesIt != listNewNodes.end(); ++newNodesIt )
5984             {
5985               myLastCreatedNodes.Append( *newNodesIt );
5986               srcNodes.Append( node );
5987             }
5988           }
5989           else
5990           {
5991             break; // newNodesItVec will be shorter than nbNodes
5992           }
5993         }
5994         newNodesItVec.push_back( nIt );
5995       }
5996       // make new elements
5997       if ( newNodesItVec.size() == nbNodes )
5998         sweepElement( elem, newNodesItVec, newElemsMap[elem], nbSteps, srcElems );
5999     }
6000   }
6001
6002   if ( theParams.ToMakeBoundary() ) {
6003     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], nbSteps, srcElems );
6004   }
6005   PGroupIDs newGroupIDs;
6006   if ( theParams.ToMakeGroups() )
6007     newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
6008
6009   return newGroupIDs;
6010 }
6011
6012 //=======================================================================
6013 //function : ExtrusionAlongTrack
6014 //purpose  :
6015 //=======================================================================
6016 SMESH_MeshEditor::Extrusion_Error
6017 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet     theElements[2],
6018                                        SMESH_subMesh*       theTrack,
6019                                        const SMDS_MeshNode* theN1,
6020                                        const bool           theHasAngles,
6021                                        list<double>&        theAngles,
6022                                        const bool           theLinearVariation,
6023                                        const bool           theHasRefPoint,
6024                                        const gp_Pnt&        theRefPoint,
6025                                        const bool           theMakeGroups)
6026 {
6027   myLastCreatedElems.Clear();
6028   myLastCreatedNodes.Clear();
6029
6030   int aNbE;
6031   std::list<double> aPrms;
6032   TIDSortedElemSet::iterator itElem;
6033
6034   gp_XYZ aGC;
6035   TopoDS_Edge aTrackEdge;
6036   TopoDS_Vertex aV1, aV2;
6037
6038   SMDS_ElemIteratorPtr aItE;
6039   SMDS_NodeIteratorPtr aItN;
6040   SMDSAbs_ElementType aTypeE;
6041
6042   TNodeOfNodeListMap mapNewNodes;
6043
6044   // 1. Check data
6045   aNbE = theElements[0].size() + theElements[1].size();
6046   // nothing to do
6047   if ( !aNbE )
6048     return EXTR_NO_ELEMENTS;
6049
6050   // 1.1 Track Pattern
6051   ASSERT( theTrack );
6052
6053   SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS();
6054   if ( !pSubMeshDS )
6055     return ExtrusionAlongTrack( theElements, theTrack->GetFather(), theN1,
6056                                 theHasAngles, theAngles, theLinearVariation,
6057                                 theHasRefPoint, theRefPoint, theMakeGroups );
6058
6059   aItE = pSubMeshDS->GetElements();
6060   while ( aItE->more() ) {
6061     const SMDS_MeshElement* pE = aItE->next();
6062     aTypeE = pE->GetType();
6063     // Pattern must contain links only
6064     if ( aTypeE != SMDSAbs_Edge )
6065       return EXTR_PATH_NOT_EDGE;
6066   }
6067
6068   list<SMESH_MeshEditor_PathPoint> fullList;
6069
6070   const TopoDS_Shape& aS = theTrack->GetSubShape();
6071   // Sub-shape for the Pattern must be an Edge or Wire
6072   if( aS.ShapeType() == TopAbs_EDGE ) {
6073     aTrackEdge = TopoDS::Edge( aS );
6074     // the Edge must not be degenerated
6075     if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
6076       return EXTR_BAD_PATH_SHAPE;
6077     TopExp::Vertices( aTrackEdge, aV1, aV2 );
6078     aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
6079     const SMDS_MeshNode* aN1 = aItN->next();
6080     aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
6081     const SMDS_MeshNode* aN2 = aItN->next();
6082     // starting node must be aN1 or aN2
6083     if ( !( aN1 == theN1 || aN2 == theN1 ) )
6084       return EXTR_BAD_STARTING_NODE;
6085     aItN = pSubMeshDS->GetNodes();
6086     while ( aItN->more() ) {
6087       const SMDS_MeshNode* pNode = aItN->next();
6088       const SMDS_EdgePosition* pEPos =
6089         static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6090       double aT = pEPos->GetUParameter();
6091       aPrms.push_back( aT );
6092     }
6093     //Extrusion_Error err =
6094     MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
6095   } else if( aS.ShapeType() == TopAbs_WIRE ) {
6096     list< SMESH_subMesh* > LSM;
6097     TopTools_SequenceOfShape Edges;
6098     SMESH_subMeshIteratorPtr itSM = theTrack->getDependsOnIterator(false,true);
6099     while(itSM->more()) {
6100       SMESH_subMesh* SM = itSM->next();
6101       LSM.push_back(SM);
6102       const TopoDS_Shape& aS = SM->GetSubShape();
6103       Edges.Append(aS);
6104     }
6105     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6106     int startNid = theN1->GetID();
6107     TColStd_MapOfInteger UsedNums;
6108
6109     int NbEdges = Edges.Length();
6110     int i = 1;
6111     for(; i<=NbEdges; i++) {
6112       int k = 0;
6113       list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
6114       for(; itLSM!=LSM.end(); itLSM++) {
6115         k++;
6116         if(UsedNums.Contains(k)) continue;
6117         aTrackEdge = TopoDS::Edge( Edges.Value(k) );
6118         SMESH_subMesh* locTrack = *itLSM;
6119         SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
6120         TopExp::Vertices( aTrackEdge, aV1, aV2 );
6121         aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
6122         const SMDS_MeshNode* aN1 = aItN->next();
6123         aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
6124         const SMDS_MeshNode* aN2 = aItN->next();
6125         // starting node must be aN1 or aN2
6126         if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
6127         // 2. Collect parameters on the track edge
6128         aPrms.clear();
6129         aItN = locMeshDS->GetNodes();
6130         while ( aItN->more() ) {
6131           const SMDS_MeshNode* pNode = aItN->next();
6132           const SMDS_EdgePosition* pEPos =
6133             static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6134           double aT = pEPos->GetUParameter();
6135           aPrms.push_back( aT );
6136         }
6137         list<SMESH_MeshEditor_PathPoint> LPP;
6138         //Extrusion_Error err =
6139         MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
6140         LLPPs.push_back(LPP);
6141         UsedNums.Add(k);
6142         // update startN for search following egde
6143         if( aN1->GetID() == startNid ) startNid = aN2->GetID();
6144         else startNid = aN1->GetID();
6145         break;
6146       }
6147     }
6148     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6149     list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
6150     list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
6151     for(; itPP!=firstList.end(); itPP++) {
6152       fullList.push_back( *itPP );
6153     }
6154     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6155     fullList.pop_back();
6156     itLLPP++;
6157     for(; itLLPP!=LLPPs.end(); itLLPP++) {
6158       list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
6159       itPP = currList.begin();
6160       SMESH_MeshEditor_PathPoint PP2 = currList.front();
6161       gp_Dir D1 = PP1.Tangent();
6162       gp_Dir D2 = PP2.Tangent();
6163       gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
6164                            (D1.Z()+D2.Z())/2 ) );
6165       PP1.SetTangent(Dnew);
6166       fullList.push_back(PP1);
6167       itPP++;
6168       for(; itPP!=firstList.end(); itPP++) {
6169         fullList.push_back( *itPP );
6170       }
6171       PP1 = fullList.back();
6172       fullList.pop_back();
6173     }
6174     // if wire not closed
6175     fullList.push_back(PP1);
6176     // else ???
6177   }
6178   else {
6179     return EXTR_BAD_PATH_SHAPE;
6180   }
6181
6182   return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
6183                           theHasRefPoint, theRefPoint, theMakeGroups);
6184 }
6185
6186
6187 //=======================================================================
6188 //function : ExtrusionAlongTrack
6189 //purpose  :
6190 //=======================================================================
6191 SMESH_MeshEditor::Extrusion_Error
6192 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet     theElements[2],
6193                                        SMESH_Mesh*          theTrack,
6194                                        const SMDS_MeshNode* theN1,
6195                                        const bool           theHasAngles,
6196                                        list<double>&        theAngles,
6197                                        const bool           theLinearVariation,
6198                                        const bool           theHasRefPoint,
6199                                        const gp_Pnt&        theRefPoint,
6200                                        const bool           theMakeGroups)
6201 {
6202   myLastCreatedElems.Clear();
6203   myLastCreatedNodes.Clear();
6204
6205   int aNbE;
6206   std::list<double> aPrms;
6207   TIDSortedElemSet::iterator itElem;
6208
6209   gp_XYZ aGC;
6210   TopoDS_Edge aTrackEdge;
6211   TopoDS_Vertex aV1, aV2;
6212
6213   SMDS_ElemIteratorPtr aItE;
6214   SMDS_NodeIteratorPtr aItN;
6215   SMDSAbs_ElementType aTypeE;
6216
6217   TNodeOfNodeListMap mapNewNodes;
6218
6219   // 1. Check data
6220   aNbE = theElements[0].size() + theElements[1].size();
6221   // nothing to do
6222   if ( !aNbE )
6223     return EXTR_NO_ELEMENTS;
6224
6225   // 1.1 Track Pattern
6226   ASSERT( theTrack );
6227
6228   SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS();
6229
6230   aItE = pMeshDS->elementsIterator();
6231   while ( aItE->more() ) {
6232     const SMDS_MeshElement* pE = aItE->next();
6233     aTypeE = pE->GetType();
6234     // Pattern must contain links only
6235     if ( aTypeE != SMDSAbs_Edge )
6236       return EXTR_PATH_NOT_EDGE;
6237   }
6238
6239   list<SMESH_MeshEditor_PathPoint> fullList;
6240
6241   const TopoDS_Shape& aS = theTrack->GetShapeToMesh();
6242
6243   if ( !theTrack->HasShapeToMesh() ) {
6244     //Mesh without shape
6245     const SMDS_MeshNode* currentNode = NULL;
6246     const SMDS_MeshNode* prevNode = theN1;
6247     std::vector<const SMDS_MeshNode*> aNodesList;
6248     aNodesList.push_back(theN1);
6249     int nbEdges = 0, conn=0;
6250     const SMDS_MeshElement* prevElem = NULL;
6251     const SMDS_MeshElement* currentElem = NULL;
6252     int totalNbEdges = theTrack->NbEdges();
6253     SMDS_ElemIteratorPtr nIt;
6254
6255     //check start node
6256     if( !theTrack->GetMeshDS()->Contains(theN1) ) {
6257       return EXTR_BAD_STARTING_NODE;
6258     }
6259
6260     conn = nbEdgeConnectivity(theN1);
6261     if( conn != 1 )
6262       return EXTR_PATH_NOT_EDGE;
6263
6264     aItE = theN1->GetInverseElementIterator();
6265     prevElem = aItE->next();
6266     currentElem = prevElem;
6267     //Get all nodes
6268     if(totalNbEdges == 1 ) {
6269       nIt = currentElem->nodesIterator();
6270       currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6271       if(currentNode == prevNode)
6272         currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6273       aNodesList.push_back(currentNode);
6274     } else {
6275       nIt = currentElem->nodesIterator();
6276       while( nIt->more() ) {
6277         currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6278         if(currentNode == prevNode)
6279           currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6280         aNodesList.push_back(currentNode);
6281
6282         //case of the closed mesh
6283         if(currentNode == theN1) {
6284           nbEdges++;
6285           break;
6286         }
6287
6288         conn = nbEdgeConnectivity(currentNode);
6289         if(conn > 2) {
6290           return EXTR_PATH_NOT_EDGE;
6291         }else if( conn == 1 && nbEdges > 0 ) {
6292           //End of the path
6293           nbEdges++;
6294           break;
6295         }else {
6296           prevNode = currentNode;
6297           aItE = currentNode->GetInverseElementIterator();
6298           currentElem = aItE->next();
6299           if( currentElem  == prevElem)
6300             currentElem = aItE->next();
6301           nIt = currentElem->nodesIterator();
6302           prevElem = currentElem;
6303           nbEdges++;
6304         }
6305       }
6306     }
6307
6308     if(nbEdges != totalNbEdges)
6309       return EXTR_PATH_NOT_EDGE;
6310
6311     TopTools_SequenceOfShape Edges;
6312     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6313     int startNid = theN1->GetID();
6314     for ( size_t i = 1; i < aNodesList.size(); i++ )
6315     {
6316       gp_Pnt     p1 = SMESH_TNodeXYZ( aNodesList[i-1] );
6317       gp_Pnt     p2 = SMESH_TNodeXYZ( aNodesList[i] );
6318       TopoDS_Edge e = BRepBuilderAPI_MakeEdge( p1, p2 );
6319       list<SMESH_MeshEditor_PathPoint> LPP;
6320       aPrms.clear();
6321       MakeEdgePathPoints(aPrms, e, (aNodesList[i-1]->GetID()==startNid), LPP);
6322       LLPPs.push_back(LPP);
6323       if ( aNodesList[i-1]->GetID() == startNid ) startNid = aNodesList[i  ]->GetID();
6324       else                                        startNid = aNodesList[i-1]->GetID();
6325     }
6326
6327     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6328     list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
6329     list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
6330     for(; itPP!=firstList.end(); itPP++) {
6331       fullList.push_back( *itPP );
6332     }
6333
6334     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6335     SMESH_MeshEditor_PathPoint PP2;
6336     fullList.pop_back();
6337     itLLPP++;
6338     for(; itLLPP!=LLPPs.end(); itLLPP++) {
6339       list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
6340       itPP = currList.begin();
6341       PP2 = currList.front();
6342       gp_Dir D1 = PP1.Tangent();
6343       gp_Dir D2 = PP2.Tangent();
6344       gp_Dir Dnew( 0.5 * ( D1.XYZ() + D2.XYZ() ));
6345       PP1.SetTangent(Dnew);
6346       fullList.push_back(PP1);
6347       itPP++;
6348       for(; itPP!=currList.end(); itPP++) {
6349         fullList.push_back( *itPP );
6350       }
6351       PP1 = fullList.back();
6352       fullList.pop_back();
6353     }
6354     fullList.push_back(PP1);
6355
6356   } // Sub-shape for the Pattern must be an Edge or Wire
6357   else if ( aS.ShapeType() == TopAbs_EDGE )
6358   {
6359     aTrackEdge = TopoDS::Edge( aS );
6360     // the Edge must not be degenerated
6361     if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
6362       return EXTR_BAD_PATH_SHAPE;
6363     TopExp::Vertices( aTrackEdge, aV1, aV2 );
6364     const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6365     const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6366     // starting node must be aN1 or aN2
6367     if ( !( aN1 == theN1 || aN2 == theN1 ) )
6368       return EXTR_BAD_STARTING_NODE;
6369     aItN = pMeshDS->nodesIterator();
6370     while ( aItN->more() ) {
6371       const SMDS_MeshNode* pNode = aItN->next();
6372       if( pNode==aN1 || pNode==aN2 ) continue;
6373       const SMDS_EdgePosition* pEPos =
6374         static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6375       double aT = pEPos->GetUParameter();
6376       aPrms.push_back( aT );
6377     }
6378     //Extrusion_Error err =
6379     MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
6380   }
6381   else if( aS.ShapeType() == TopAbs_WIRE ) {
6382     list< SMESH_subMesh* > LSM;
6383     TopTools_SequenceOfShape Edges;
6384     TopExp_Explorer eExp(aS, TopAbs_EDGE);
6385     for(; eExp.More(); eExp.Next()) {
6386       TopoDS_Edge E = TopoDS::Edge( eExp.Current() );
6387       if( SMESH_Algo::isDegenerated(E) ) continue;
6388       SMESH_subMesh* SM = theTrack->GetSubMesh(E);
6389       if(SM) {
6390         LSM.push_back(SM);
6391         Edges.Append(E);
6392       }
6393     }
6394     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6395     TopoDS_Vertex aVprev;
6396     TColStd_MapOfInteger UsedNums;
6397     int NbEdges = Edges.Length();
6398     int i = 1;
6399     for(; i<=NbEdges; i++) {
6400       int k = 0;
6401       list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
6402       for(; itLSM!=LSM.end(); itLSM++) {
6403         k++;
6404         if(UsedNums.Contains(k)) continue;
6405         aTrackEdge = TopoDS::Edge( Edges.Value(k) );
6406         SMESH_subMesh* locTrack = *itLSM;
6407         SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
6408         TopExp::Vertices( aTrackEdge, aV1, aV2 );
6409         bool aN1isOK = false, aN2isOK = false;
6410         if ( aVprev.IsNull() ) {
6411           // if previous vertex is not yet defined, it means that we in the beginning of wire
6412           // and we have to find initial vertex corresponding to starting node theN1
6413           const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6414           const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6415           // starting node must be aN1 or aN2
6416           aN1isOK = ( aN1 && aN1 == theN1 );
6417           aN2isOK = ( aN2 && aN2 == theN1 );
6418         }
6419         else {
6420           // we have specified ending vertex of the previous edge on the previous iteration
6421           // and we have just to check that it corresponds to any vertex in current segment
6422           aN1isOK = aVprev.IsSame( aV1 );
6423           aN2isOK = aVprev.IsSame( aV2 );
6424         }
6425         if ( !aN1isOK && !aN2isOK ) continue;
6426         // 2. Collect parameters on the track edge
6427         aPrms.clear();
6428         aItN = locMeshDS->GetNodes();
6429         while ( aItN->more() ) {
6430           const SMDS_MeshNode*     pNode = aItN->next();
6431           const SMDS_EdgePosition* pEPos =
6432             static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6433           double aT = pEPos->GetUParameter();
6434           aPrms.push_back( aT );
6435         }
6436         list<SMESH_MeshEditor_PathPoint> LPP;
6437         //Extrusion_Error err =
6438         MakeEdgePathPoints(aPrms, aTrackEdge, aN1isOK, LPP);
6439         LLPPs.push_back(LPP);
6440         UsedNums.Add(k);
6441         // update startN for search following egde
6442         if ( aN1isOK ) aVprev = aV2;
6443         else           aVprev = aV1;
6444         break;
6445       }
6446     }
6447     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6448     list<SMESH_MeshEditor_PathPoint>& firstList = *itLLPP;
6449     fullList.splice( fullList.end(), firstList );
6450
6451     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6452     fullList.pop_back();
6453     itLLPP++;
6454     for(; itLLPP!=LLPPs.end(); itLLPP++) {
6455       list<SMESH_MeshEditor_PathPoint>& currList = *itLLPP;
6456       SMESH_MeshEditor_PathPoint PP2 = currList.front();
6457       gp_Dir D1 = PP1.Tangent();
6458       gp_Dir D2 = PP2.Tangent();
6459       gp_Dir Dnew( D1.XYZ() + D2.XYZ() );
6460       PP1.SetTangent(Dnew);
6461       fullList.push_back(PP1);
6462       fullList.splice( fullList.end(), currList, ++currList.begin(), currList.end() );
6463       PP1 = fullList.back();
6464       fullList.pop_back();
6465     }
6466     // if wire not closed
6467     fullList.push_back(PP1);
6468     // else ???
6469   }
6470   else {
6471     return EXTR_BAD_PATH_SHAPE;
6472   }
6473
6474   return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
6475                           theHasRefPoint, theRefPoint, theMakeGroups);
6476 }
6477
6478
6479 //=======================================================================
6480 //function : MakeEdgePathPoints
6481 //purpose  : auxilary for ExtrusionAlongTrack
6482 //=======================================================================
6483 SMESH_MeshEditor::Extrusion_Error
6484 SMESH_MeshEditor::MakeEdgePathPoints(std::list<double>&                aPrms,
6485                                      const TopoDS_Edge&                aTrackEdge,
6486                                      bool                              FirstIsStart,
6487                                      list<SMESH_MeshEditor_PathPoint>& LPP)
6488 {
6489   Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2;
6490   aTolVec=1.e-7;
6491   aTolVec2=aTolVec*aTolVec;
6492   double aT1, aT2;
6493   TopoDS_Vertex aV1, aV2;
6494   TopExp::Vertices( aTrackEdge, aV1, aV2 );
6495   aT1=BRep_Tool::Parameter( aV1, aTrackEdge );
6496   aT2=BRep_Tool::Parameter( aV2, aTrackEdge );
6497   // 2. Collect parameters on the track edge
6498   aPrms.push_front( aT1 );
6499   aPrms.push_back( aT2 );
6500   // sort parameters
6501   aPrms.sort();
6502   if( FirstIsStart ) {
6503     if ( aT1 > aT2 ) {
6504       aPrms.reverse();
6505     }
6506   }
6507   else {
6508     if ( aT2 > aT1 ) {
6509       aPrms.reverse();
6510     }
6511   }
6512   // 3. Path Points
6513   SMESH_MeshEditor_PathPoint aPP;
6514   Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 );
6515   std::list<double>::iterator aItD = aPrms.begin();
6516   for(; aItD != aPrms.end(); ++aItD) {
6517     double aT = *aItD;
6518     gp_Pnt aP3D;
6519     gp_Vec aVec;
6520     aC3D->D1( aT, aP3D, aVec );
6521     aL2 = aVec.SquareMagnitude();
6522     if ( aL2 < aTolVec2 )
6523       return EXTR_CANT_GET_TANGENT;
6524     gp_Dir aTgt( FirstIsStart ? aVec : -aVec );
6525     aPP.SetPnt( aP3D );
6526     aPP.SetTangent( aTgt );
6527     aPP.SetParameter( aT );
6528     LPP.push_back(aPP);
6529   }
6530   return EXTR_OK;
6531 }
6532
6533
6534 //=======================================================================
6535 //function : MakeExtrElements
6536 //purpose  : auxilary for ExtrusionAlongTrack
6537 //=======================================================================
6538 SMESH_MeshEditor::Extrusion_Error
6539 SMESH_MeshEditor::MakeExtrElements(TIDSortedElemSet                  theElemSets[2],
6540                                    list<SMESH_MeshEditor_PathPoint>& fullList,
6541                                    const bool                        theHasAngles,
6542                                    list<double>&                     theAngles,
6543                                    const bool                        theLinearVariation,
6544                                    const bool                        theHasRefPoint,
6545                                    const gp_Pnt&                     theRefPoint,
6546                                    const bool                        theMakeGroups)
6547 {
6548   const int aNbTP = fullList.size();
6549
6550   // Angles
6551   if( theHasAngles && !theAngles.empty() && theLinearVariation )
6552     LinearAngleVariation(aNbTP-1, theAngles);
6553
6554   // fill vector of path points with angles
6555   vector<SMESH_MeshEditor_PathPoint> aPPs;
6556   list<SMESH_MeshEditor_PathPoint>::iterator itPP = fullList.begin();
6557   list<double>::iterator                 itAngles = theAngles.begin();
6558   aPPs.push_back( *itPP++ );
6559   for( ; itPP != fullList.end(); itPP++) {
6560     aPPs.push_back( *itPP );
6561     if ( theHasAngles && itAngles != theAngles.end() )
6562       aPPs.back().SetAngle( *itAngles++ );
6563   }
6564
6565   TNodeOfNodeListMap   mapNewNodes;
6566   TElemOfVecOfNnlmiMap mapElemNewNodes;
6567   TTElemOfElemListMap  newElemsMap;
6568   TIDSortedElemSet::iterator itElem;
6569   // source elements for each generated one
6570   SMESH_SequenceOfElemPtr srcElems, srcNodes;
6571
6572   // 3. Center of rotation aV0
6573   gp_Pnt aV0 = theRefPoint;
6574   if ( !theHasRefPoint )
6575   {
6576     gp_XYZ aGC( 0.,0.,0. );
6577     TIDSortedElemSet newNodes;
6578
6579     for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6580     {
6581       TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6582       itElem = theElements.begin();
6583       for ( ; itElem != theElements.end(); itElem++ )
6584       {
6585         const SMDS_MeshElement* elem = *itElem;
6586         SMDS_ElemIteratorPtr     itN = elem->nodesIterator();
6587         while ( itN->more() ) {
6588           const SMDS_MeshElement* node = itN->next();
6589           if ( newNodes.insert( node ).second )
6590             aGC += SMESH_TNodeXYZ( node );
6591         }
6592       }
6593     }
6594     aGC /= newNodes.size();
6595     aV0.SetXYZ( aGC );
6596   } // if (!theHasRefPoint) {
6597
6598   // 4. Processing the elements
6599   SMESHDS_Mesh* aMesh = GetMeshDS();
6600   list<const SMDS_MeshNode*> emptyList;
6601
6602   setElemsFirst( theElemSets );
6603   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6604   {
6605     TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6606     for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ )
6607     {
6608       const SMDS_MeshElement* elem = *itElem;
6609
6610       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
6611       newNodesItVec.reserve( elem->NbNodes() );
6612
6613       // loop on elem nodes
6614       int nodeIndex = -1;
6615       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6616       while ( itN->more() )
6617       {
6618         ++nodeIndex;
6619         // check if a node has been already processed
6620         const SMDS_MeshNode* node = cast2Node( itN->next() );
6621         TNodeOfNodeListMap::iterator nIt = mapNewNodes.insert( make_pair( node, emptyList )).first;
6622         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
6623         if ( listNewNodes.empty() )
6624         {
6625           // make new nodes
6626           Standard_Real aAngle1x, aAngleT1T0, aTolAng;
6627           gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
6628           gp_Ax1 anAx1, anAxT1T0;
6629           gp_Dir aDT1x, aDT0x, aDT1T0;
6630
6631           aTolAng=1.e-4;
6632
6633           aV0x = aV0;
6634           aPN0 = SMESH_TNodeXYZ( node );
6635
6636           const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
6637           aP0x = aPP0.Pnt();
6638           aDT0x= aPP0.Tangent();
6639
6640           for ( int j = 1; j < aNbTP; ++j ) {
6641             const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
6642             aP1x     = aPP1.Pnt();
6643             aDT1x    = aPP1.Tangent();
6644             aAngle1x = aPP1.Angle();
6645
6646             gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
6647             // Translation
6648             gp_Vec aV01x( aP0x, aP1x );
6649             aTrsf.SetTranslation( aV01x );
6650
6651             // traslated point
6652             aV1x = aV0x.Transformed( aTrsf );
6653             aPN1 = aPN0.Transformed( aTrsf );
6654
6655             // rotation 1 [ T1,T0 ]
6656             aAngleT1T0=-aDT1x.Angle( aDT0x );
6657             if (fabs(aAngleT1T0) > aTolAng)
6658             {
6659               aDT1T0=aDT1x^aDT0x;
6660               anAxT1T0.SetLocation( aV1x );
6661               anAxT1T0.SetDirection( aDT1T0 );
6662               aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
6663
6664               aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
6665             }
6666
6667             // rotation 2
6668             if ( theHasAngles ) {
6669               anAx1.SetLocation( aV1x );
6670               anAx1.SetDirection( aDT1x );
6671               aTrsfRot.SetRotation( anAx1, aAngle1x );
6672
6673               aPN1 = aPN1.Transformed( aTrsfRot );
6674             }
6675
6676             // make new node
6677             if ( elem->IsQuadratic() && !elem->IsMediumNode(node) )
6678             {
6679               // create additional node
6680               gp_XYZ midP = 0.5 * ( aPN1.XYZ() + aPN0.XYZ() );
6681               const SMDS_MeshNode* newNode = aMesh->AddNode( midP.X(), midP.Y(), midP.Z() );
6682               myLastCreatedNodes.Append(newNode);
6683               srcNodes.Append( node );
6684               listNewNodes.push_back( newNode );
6685             }
6686             const SMDS_MeshNode* newNode = aMesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
6687             myLastCreatedNodes.Append(newNode);
6688             srcNodes.Append( node );
6689             listNewNodes.push_back( newNode );
6690
6691             aPN0 = aPN1;
6692             aP0x = aP1x;
6693             aV0x = aV1x;
6694             aDT0x = aDT1x;
6695           }
6696         }
6697         else if( elem->IsQuadratic() && !elem->IsMediumNode(node) )
6698         {
6699           // if current elem is quadratic and current node is not medium
6700           // we have to check - may be it is needed to insert additional nodes
6701           list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
6702           if ((int) listNewNodes.size() == aNbTP-1 )
6703           {
6704             vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
6705             gp_XYZ P(node->X(), node->Y(), node->Z());
6706             list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
6707             int i;
6708             for(i=0; i<aNbTP-1; i++) {
6709               const SMDS_MeshNode* N = *it;
6710               double x = ( N->X() + P.X() )/2.;
6711               double y = ( N->Y() + P.Y() )/2.;
6712               double z = ( N->Z() + P.Z() )/2.;
6713               const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
6714               srcNodes.Append( node );
6715               myLastCreatedNodes.Append(newN);
6716               aNodes[2*i] = newN;
6717               aNodes[2*i+1] = N;
6718               P = gp_XYZ(N->X(),N->Y(),N->Z());
6719             }
6720             listNewNodes.clear();
6721             for(i=0; i<2*(aNbTP-1); i++) {
6722               listNewNodes.push_back(aNodes[i]);
6723             }
6724           }
6725         }
6726
6727         newNodesItVec.push_back( nIt );
6728       }
6729
6730       // make new elements
6731       sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
6732     }
6733   }
6734
6735   makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], aNbTP-1, srcElems );
6736
6737   if ( theMakeGroups )
6738     generateGroups( srcNodes, srcElems, "extruded");
6739
6740   return EXTR_OK;
6741 }
6742
6743
6744 //=======================================================================
6745 //function : LinearAngleVariation
6746 //purpose  : spread values over nbSteps
6747 //=======================================================================
6748
6749 void SMESH_MeshEditor::LinearAngleVariation(const int     nbSteps,
6750                                             list<double>& Angles)
6751 {
6752   int nbAngles = Angles.size();
6753   if( nbSteps > nbAngles && nbAngles > 0 )
6754   {
6755     vector<double> theAngles(nbAngles);
6756     theAngles.assign( Angles.begin(), Angles.end() );
6757
6758     list<double> res;
6759     double rAn2St = double( nbAngles ) / double( nbSteps );
6760     double angPrev = 0, angle;
6761     for ( int iSt = 0; iSt < nbSteps; ++iSt )
6762     {
6763       double angCur = rAn2St * ( iSt+1 );
6764       double angCurFloor  = floor( angCur );
6765       double angPrevFloor = floor( angPrev );
6766       if ( angPrevFloor == angCurFloor )
6767         angle = rAn2St * theAngles[ int( angCurFloor ) ];
6768       else {
6769         int iP = int( angPrevFloor );
6770         double angPrevCeil = ceil(angPrev);
6771         angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6772
6773         int iC = int( angCurFloor );
6774         if ( iC < nbAngles )
6775           angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6776
6777         iP = int( angPrevCeil );
6778         while ( iC-- > iP )
6779           angle += theAngles[ iC ];
6780       }
6781       res.push_back(angle);
6782       angPrev = angCur;
6783     }
6784     Angles.swap( res );
6785   }
6786 }
6787
6788
6789 //================================================================================
6790 /*!
6791  * \brief Move or copy theElements applying theTrsf to their nodes
6792  *  \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6793  *  \param theTrsf - transformation to apply
6794  *  \param theCopy - if true, create translated copies of theElems
6795  *  \param theMakeGroups - if true and theCopy, create translated groups
6796  *  \param theTargetMesh - mesh to copy translated elements into
6797  *  \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6798  */
6799 //================================================================================
6800
6801 SMESH_MeshEditor::PGroupIDs
6802 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6803                              const gp_Trsf&     theTrsf,
6804                              const bool         theCopy,
6805                              const bool         theMakeGroups,
6806                              SMESH_Mesh*        theTargetMesh)
6807 {
6808   myLastCreatedElems.Clear();
6809   myLastCreatedNodes.Clear();
6810
6811   bool needReverse = false;
6812   string groupPostfix;
6813   switch ( theTrsf.Form() ) {
6814   case gp_PntMirror:
6815     needReverse = true;
6816     groupPostfix = "mirrored";
6817     break;
6818   case gp_Ax1Mirror:
6819     groupPostfix = "mirrored";
6820     break;
6821   case gp_Ax2Mirror:
6822     needReverse = true;
6823     groupPostfix = "mirrored";
6824     break;
6825   case gp_Rotation:
6826     groupPostfix = "rotated";
6827     break;
6828   case gp_Translation:
6829     groupPostfix = "translated";
6830     break;
6831   case gp_Scale:
6832     groupPostfix = "scaled";
6833     break;
6834   case gp_CompoundTrsf: // different scale by axis
6835     groupPostfix = "scaled";
6836     break;
6837   default:
6838     needReverse = false;
6839     groupPostfix = "transformed";
6840   }
6841
6842   SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6843   SMESHDS_Mesh* aMesh    = GetMeshDS();
6844
6845   SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6846   SMESH_MeshEditor* editor = theTargetMesh ? & targetMeshEditor : theCopy ? this : 0;
6847   SMESH_MeshEditor::ElemFeatures elemType;
6848
6849   // map old node to new one
6850   TNodeNodeMap nodeMap;
6851
6852   // elements sharing moved nodes; those of them which have all
6853   // nodes mirrored but are not in theElems are to be reversed
6854   TIDSortedElemSet inverseElemSet;
6855
6856   // source elements for each generated one
6857   SMESH_SequenceOfElemPtr srcElems, srcNodes;
6858
6859   // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6860   TIDSortedElemSet orphanNode;
6861
6862   if ( theElems.empty() ) // transform the whole mesh
6863   {
6864     // add all elements
6865     SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6866     while ( eIt->more() ) theElems.insert( eIt->next() );
6867     // add orphan nodes
6868     SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6869     while ( nIt->more() )
6870     {
6871       const SMDS_MeshNode* node = nIt->next();
6872       if ( node->NbInverseElements() == 0)
6873         orphanNode.insert( node );
6874     }
6875   }
6876
6877   // loop on elements to transform nodes : first orphan nodes then elems
6878   TIDSortedElemSet::iterator itElem;
6879   TIDSortedElemSet *elements[] = { &orphanNode, &theElems };
6880   for (int i=0; i<2; i++)
6881     for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ )
6882     {
6883       const SMDS_MeshElement* elem = *itElem;
6884       if ( !elem )
6885         continue;
6886
6887       // loop on elem nodes
6888       double coord[3];
6889       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6890       while ( itN->more() )
6891       {
6892         const SMDS_MeshNode* node = cast2Node( itN->next() );
6893         // check if a node has been already transformed
6894         pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6895           nodeMap.insert( make_pair ( node, node ));
6896         if ( !n2n_isnew.second )
6897           continue;
6898
6899         node->GetXYZ( coord );
6900         theTrsf.Transforms( coord[0], coord[1], coord[2] );
6901         if ( theTargetMesh ) {
6902           const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6903           n2n_isnew.first->second = newNode;
6904           myLastCreatedNodes.Append(newNode);
6905           srcNodes.Append( node );
6906         }
6907         else if ( theCopy ) {
6908           const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6909           n2n_isnew.first->second = newNode;
6910           myLastCreatedNodes.Append(newNode);
6911           srcNodes.Append( node );
6912         }
6913         else {
6914           aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6915           // node position on shape becomes invalid
6916           const_cast< SMDS_MeshNode* > ( node )->SetPosition
6917             ( SMDS_SpacePosition::originSpacePosition() );
6918         }
6919
6920         // keep inverse elements
6921         if ( !theCopy && !theTargetMesh && needReverse ) {
6922           SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6923           while ( invElemIt->more() ) {
6924             const SMDS_MeshElement* iel = invElemIt->next();
6925             inverseElemSet.insert( iel );
6926           }
6927         }
6928       }
6929     } // loop on elems in { &orphanNode, &theElems };
6930
6931   // either create new elements or reverse mirrored ones
6932   if ( !theCopy && !needReverse && !theTargetMesh )
6933     return PGroupIDs();
6934
6935   theElems.insert( inverseElemSet.begin(),inverseElemSet.end() );
6936
6937   // Replicate or reverse elements
6938
6939   std::vector<int> iForw;
6940   vector<const SMDS_MeshNode*> nodes;
6941   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6942   {
6943     const SMDS_MeshElement* elem = *itElem;
6944     if ( !elem ) continue;
6945
6946     SMDSAbs_GeometryType geomType = elem->GetGeomType();
6947     size_t               nbNodes  = elem->NbNodes();
6948     if ( geomType == SMDSGeom_NONE ) continue; // node
6949
6950     nodes.resize( nbNodes );
6951
6952     if ( geomType == SMDSGeom_POLYHEDRA )  // ------------------ polyhedral volume
6953     {
6954       const SMDS_VtkVolume* aPolyedre = dynamic_cast<const SMDS_VtkVolume*>( elem );
6955       if (!aPolyedre)
6956         continue;
6957       nodes.clear();
6958       bool allTransformed = true;
6959       int nbFaces = aPolyedre->NbFaces();
6960       for (int iface = 1; iface <= nbFaces && allTransformed; iface++)
6961       {
6962         int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6963         for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++)
6964         {
6965           const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6966           TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6967           if ( nodeMapIt == nodeMap.end() )
6968             allTransformed = false; // not all nodes transformed
6969           else
6970             nodes.push_back((*nodeMapIt).second);
6971         }
6972         if ( needReverse && allTransformed )
6973           std::reverse( nodes.end() - nbFaceNodes, nodes.end() );
6974       }
6975       if ( !allTransformed )
6976         continue; // not all nodes transformed
6977     }
6978     else // ----------------------- the rest element types
6979     {
6980       while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6981       const vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType(), nbNodes );
6982       const vector<int>&    i = needReverse ? iRev : iForw;
6983
6984       // find transformed nodes
6985       size_t iNode = 0;
6986       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6987       while ( itN->more() ) {
6988         const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
6989         TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6990         if ( nodeMapIt == nodeMap.end() )
6991           break; // not all nodes transformed
6992         nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
6993       }
6994       if ( iNode != nbNodes )
6995         continue; // not all nodes transformed
6996     }
6997
6998     if ( editor ) {
6999       // copy in this or a new mesh
7000       if ( editor->AddElement( nodes, elemType.Init( elem, /*basicOnly=*/false )))
7001         srcElems.Append( elem );
7002     }
7003     else {
7004       // reverse element as it was reversed by transformation
7005       if ( nbNodes > 2 )
7006         aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
7007     }
7008
7009   } // loop on elements
7010
7011   if ( editor && editor != this )
7012     myLastCreatedElems = editor->myLastCreatedElems;
7013
7014   PGroupIDs newGroupIDs;
7015
7016   if ( ( theMakeGroups && theCopy ) ||
7017        ( theMakeGroups && theTargetMesh ) )
7018     newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh, false );
7019
7020   return newGroupIDs;
7021 }
7022
7023 //=======================================================================
7024 /*!
7025  * \brief Create groups of elements made during transformation
7026  *  \param nodeGens - nodes making corresponding myLastCreatedNodes
7027  *  \param elemGens - elements making corresponding myLastCreatedElems
7028  *  \param postfix - to append to names of new groups
7029  *  \param targetMesh - mesh to create groups in
7030  *  \param topPresent - is there "top" elements that are created by sweeping
7031  */
7032 //=======================================================================
7033
7034 SMESH_MeshEditor::PGroupIDs
7035 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
7036                                  const SMESH_SequenceOfElemPtr& elemGens,
7037                                  const std::string&             postfix,
7038                                  SMESH_Mesh*                    targetMesh,
7039                                  const bool                     topPresent)
7040 {
7041   PGroupIDs newGroupIDs( new list<int> );
7042   SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
7043
7044   // Sort existing groups by types and collect their names
7045
7046   // containers to store an old group and generated new ones;
7047   // 1st new group is for result elems of different type than a source one;
7048   // 2nd new group is for same type result elems ("top" group at extrusion)
7049   using boost::tuple;
7050   using boost::make_tuple;
7051   typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
7052   vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
7053   vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
7054   // group names
7055   set< string > groupNames;
7056
7057   SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
7058   if ( !groupIt->more() ) return newGroupIDs;
7059
7060   int newGroupID = mesh->GetGroupIds().back()+1;
7061   while ( groupIt->more() )
7062   {
7063     SMESH_Group * group = groupIt->next();
7064     if ( !group ) continue;
7065     SMESHDS_GroupBase* groupDS = group->GetGroupDS();
7066     if ( !groupDS || groupDS->IsEmpty() ) continue;
7067     groupNames.insert    ( group->GetName() );
7068     groupDS->SetStoreName( group->GetName() );
7069     const SMDSAbs_ElementType type = groupDS->GetType();
7070     SMESHDS_Group* newGroup    = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
7071     SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
7072     groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
7073     orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
7074   }
7075
7076   // Loop on nodes and elements to add them in new groups
7077
7078   vector< const SMDS_MeshElement* > resultElems;
7079   for ( int isNodes = 0; isNodes < 2; ++isNodes )
7080   {
7081     const SMESH_SequenceOfElemPtr& gens  = isNodes ? nodeGens : elemGens;
7082     const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
7083     if ( gens.Length() != elems.Length() )
7084       throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
7085
7086     // loop on created elements
7087     for (int iElem = 1; iElem <= elems.Length(); ++iElem )
7088     {
7089       const SMDS_MeshElement* sourceElem = gens( iElem );
7090       if ( !sourceElem ) {
7091         MESSAGE("generateGroups(): NULL source element");
7092         continue;
7093       }
7094       list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
7095       if ( groupsOldNew.empty() ) { // no groups of this type at all
7096         while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
7097           ++iElem; // skip all elements made by sourceElem
7098         continue;
7099       }
7100       // collect all elements made by the iElem-th sourceElem
7101       resultElems.clear();
7102       if ( const SMDS_MeshElement* resElem = elems( iElem ))
7103         if ( resElem != sourceElem )
7104           resultElems.push_back( resElem );
7105       while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
7106         if ( const SMDS_MeshElement* resElem = elems( ++iElem ))
7107           if ( resElem != sourceElem )
7108             resultElems.push_back( resElem );
7109
7110       const SMDS_MeshElement* topElem = 0;
7111       if ( isNodes ) // there must be a top element
7112       {
7113         topElem = resultElems.back();
7114         resultElems.pop_back();
7115       }
7116       else
7117       {
7118         vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
7119         for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
7120           if ( (*resElemIt)->GetType() == sourceElem->GetType() )
7121           {
7122             topElem = *resElemIt;
7123             *resElemIt = 0; // erase *resElemIt
7124             break;
7125           }
7126       }
7127       // add resultElems to groups originted from ones the sourceElem belongs to
7128       list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
7129       for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
7130       {
7131         SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
7132         if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
7133         {
7134           // fill in a new group
7135           SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
7136           vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
7137           for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
7138             if ( *resElemIt )
7139               newGroup.Add( *resElemIt );
7140
7141           // fill a "top" group
7142           if ( topElem )
7143           {
7144             SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
7145             newTopGroup.Add( topElem );
7146          }
7147         }
7148       }
7149     } // loop on created elements
7150   }// loop on nodes and elements
7151
7152   // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
7153
7154   list<int> topGrouIds;
7155   for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
7156   {
7157     SMESHDS_GroupBase* oldGroupDS =   orderedOldNewGroups[i]->get<0>();
7158     SMESHDS_Group*   newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
7159                                       orderedOldNewGroups[i]->get<2>() };
7160     for ( int is2nd = 0; is2nd < 2; ++is2nd )
7161     {
7162       SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
7163       if ( newGroupDS->IsEmpty() )
7164       {
7165         mesh->GetMeshDS()->RemoveGroup( newGroupDS );
7166       }
7167       else
7168       {
7169         // set group type
7170         newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
7171
7172         // make a name
7173         const bool isTop = ( topPresent &&
7174                              newGroupDS->GetType() == oldGroupDS->GetType() &&
7175                              is2nd );
7176
7177         string name = oldGroupDS->GetStoreName();
7178         { // remove trailing whitespaces (issue 22599)
7179           size_t size = name.size();
7180           while ( size > 1 && isspace( name[ size-1 ]))
7181             --size;
7182           if ( size != name.size() )
7183           {
7184             name.resize( size );
7185             oldGroupDS->SetStoreName( name.c_str() );
7186           }
7187         }
7188         if ( !targetMesh ) {
7189           string suffix = ( isTop ? "top": postfix.c_str() );
7190           name += "_";
7191           name += suffix;
7192           int nb = 1;
7193           while ( !groupNames.insert( name ).second ) // name exists
7194             name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
7195         }
7196         else if ( isTop ) {
7197           name += "_top";
7198         }
7199         newGroupDS->SetStoreName( name.c_str() );
7200
7201         // make a SMESH_Groups
7202         mesh->AddGroup( newGroupDS );
7203         if ( isTop )
7204           topGrouIds.push_back( newGroupDS->GetID() );
7205         else
7206           newGroupIDs->push_back( newGroupDS->GetID() );
7207       }
7208     }
7209   }
7210   newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
7211
7212   return newGroupIDs;
7213 }
7214
7215 //================================================================================
7216 /*!
7217  *  * \brief Return list of group of nodes close to each other within theTolerance
7218  *  *        Search among theNodes or in the whole mesh if theNodes is empty using
7219  *  *        an Octree algorithm
7220  *  \param [in,out] theNodes - the nodes to treat
7221  *  \param [in]     theTolerance - the tolerance
7222  *  \param [out]    theGroupsOfNodes - the result groups of coincident nodes
7223  *  \param [in]     theSeparateCornersAndMedium - if \c true, in quadratic mesh puts 
7224  *         corner and medium nodes in separate groups
7225  */
7226 //================================================================================
7227
7228 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet &   theNodes,
7229                                             const double         theTolerance,
7230                                             TListOfListOfNodes & theGroupsOfNodes,
7231                                             bool                 theSeparateCornersAndMedium)
7232 {
7233   myLastCreatedElems.Clear();
7234   myLastCreatedNodes.Clear();
7235
7236   if ( myMesh->NbEdges  ( ORDER_QUADRATIC ) +
7237        myMesh->NbFaces  ( ORDER_QUADRATIC ) +
7238        myMesh->NbVolumes( ORDER_QUADRATIC ) == 0 )
7239     theSeparateCornersAndMedium = false;
7240
7241   TIDSortedNodeSet& corners = theNodes;
7242   TIDSortedNodeSet  medium;
7243
7244   if ( theNodes.empty() ) // get all nodes in the mesh
7245   {
7246     TIDSortedNodeSet* nodes[2] = { &corners, &medium };
7247     SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(/*idInceasingOrder=*/true);
7248     if ( theSeparateCornersAndMedium )
7249       while ( nIt->more() )
7250       {
7251         const SMDS_MeshNode* n = nIt->next();
7252         TIDSortedNodeSet* & nodeSet = nodes[ SMESH_MesherHelper::IsMedium( n )];
7253         nodeSet->insert( nodeSet->end(), n );
7254       }
7255     else
7256       while ( nIt->more() )
7257         theNodes.insert( theNodes.end(),nIt->next() );
7258   }
7259   else if ( theSeparateCornersAndMedium ) // separate corners from medium nodes
7260   {
7261     TIDSortedNodeSet::iterator nIt = corners.begin();
7262     while ( nIt != corners.end() )
7263       if ( SMESH_MesherHelper::IsMedium( *nIt ))
7264       {
7265         medium.insert( medium.end(), *nIt );
7266         corners.erase( nIt++ );
7267       }
7268       else
7269       {
7270         ++nIt;
7271       }
7272   }
7273
7274   if ( !corners.empty() )
7275     SMESH_OctreeNode::FindCoincidentNodes ( corners, &theGroupsOfNodes, theTolerance );
7276   if ( !medium.empty() )
7277     SMESH_OctreeNode::FindCoincidentNodes ( medium, &theGroupsOfNodes, theTolerance );
7278 }
7279
7280 //=======================================================================
7281 //function : SimplifyFace
7282 //purpose  : split a chain of nodes into several closed chains
7283 //=======================================================================
7284
7285 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
7286                                     vector<const SMDS_MeshNode *>&       poly_nodes,
7287                                     vector<int>&                         quantities) const
7288 {
7289   int nbNodes = faceNodes.size();
7290   while ( faceNodes[ 0 ] == faceNodes[ nbNodes-1 ] && nbNodes > 2 )
7291     --nbNodes;
7292   if ( nbNodes < 3 )
7293     return 0;
7294   size_t prevNbQuant = quantities.size();
7295
7296   vector< const SMDS_MeshNode* > simpleNodes; simpleNodes.reserve( nbNodes );
7297   map< const SMDS_MeshNode*, int > nodeIndices; // indices within simpleNodes
7298   map< const SMDS_MeshNode*, int >::iterator nInd;
7299
7300   nodeIndices.insert( make_pair( faceNodes[0], 0 ));
7301   simpleNodes.push_back( faceNodes[0] );
7302   for ( int iCur = 1; iCur < nbNodes; iCur++ )
7303   {
7304     if ( faceNodes[ iCur ] != simpleNodes.back() )
7305     {
7306       int index = simpleNodes.size();
7307       nInd = nodeIndices.insert( make_pair( faceNodes[ iCur ], index )).first;
7308       int prevIndex = nInd->second;
7309       if ( prevIndex < index )
7310       {
7311         // a sub-loop found
7312         int loopLen = index - prevIndex;
7313         if ( loopLen > 2 )
7314         {
7315           // store the sub-loop
7316           quantities.push_back( loopLen );
7317           for ( int i = prevIndex; i < index; i++ )
7318             poly_nodes.push_back( simpleNodes[ i ]);
7319         }
7320         simpleNodes.resize( prevIndex+1 );
7321       }
7322       else
7323       {
7324         simpleNodes.push_back( faceNodes[ iCur ]);
7325       }
7326     }
7327   }
7328
7329   if ( simpleNodes.size() > 2 )
7330   {
7331     quantities.push_back( simpleNodes.size() );
7332     poly_nodes.insert ( poly_nodes.end(), simpleNodes.begin(), simpleNodes.end() );
7333   }
7334
7335   return quantities.size() - prevNbQuant;
7336 }
7337
7338 //=======================================================================
7339 //function : MergeNodes
7340 //purpose  : In each group, the cdr of nodes are substituted by the first one
7341 //           in all elements.
7342 //=======================================================================
7343
7344 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes)
7345 {
7346   myLastCreatedElems.Clear();
7347   myLastCreatedNodes.Clear();
7348
7349   SMESHDS_Mesh* aMesh = GetMeshDS();
7350
7351   TNodeNodeMap nodeNodeMap; // node to replace - new node
7352   set<const SMDS_MeshElement*> elems; // all elements with changed nodes
7353   list< int > rmElemIds, rmNodeIds;
7354
7355   // Fill nodeNodeMap and elems
7356
7357   TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
7358   for ( ; grIt != theGroupsOfNodes.end(); grIt++ )
7359   {
7360     list<const SMDS_MeshNode*>& nodes = *grIt;
7361     list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
7362     const SMDS_MeshNode* nToKeep = *nIt;
7363     for ( ++nIt; nIt != nodes.end(); nIt++ )
7364     {
7365       const SMDS_MeshNode* nToRemove = *nIt;
7366       nodeNodeMap.insert( make_pair( nToRemove, nToKeep ));
7367       if ( nToRemove != nToKeep )
7368       {
7369         rmNodeIds.push_back( nToRemove->GetID() );
7370         AddToSameGroups( nToKeep, nToRemove, aMesh );
7371         // set _alwaysComputed to a sub-mesh of VERTEX to enable mesh computing
7372         // after MergeNodes() w/o creating node in place of merged ones.
7373         const SMDS_PositionPtr& pos = nToRemove->GetPosition();
7374         if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
7375           if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
7376             sm->SetIsAlwaysComputed( true );
7377       }
7378       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
7379       while ( invElemIt->more() ) {
7380         const SMDS_MeshElement* elem = invElemIt->next();
7381         elems.insert(elem);
7382       }
7383     }
7384   }
7385   // Change element nodes or remove an element
7386
7387   set<const SMDS_MeshNode*> nodeSet;
7388   vector< const SMDS_MeshNode*> curNodes, uniqueNodes;
7389   vector<int> iRepl;
7390   ElemFeatures elemType;
7391
7392   set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7393   for ( ; eIt != elems.end(); eIt++ )
7394   {
7395     const SMDS_MeshElement* elem = *eIt;
7396     const           int  nbNodes = elem->NbNodes();
7397     const           int aShapeId = FindShape( elem );
7398     SMDSAbs_EntityType    entity = elem->GetEntityType();
7399
7400     nodeSet.clear();
7401     curNodes.resize( nbNodes );
7402     uniqueNodes.resize( nbNodes );
7403     iRepl.resize( nbNodes );
7404     int iUnique = 0, iCur = 0, nbRepl = 0;
7405
7406     // get new seq of nodes
7407     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7408     while ( itN->more() )
7409     {
7410       const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7411
7412       TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7413       if ( nnIt != nodeNodeMap.end() ) { // n sticks
7414         n = (*nnIt).second;
7415         { ////////// BUG 0020185: begin
7416           bool stopRecur = false;
7417           set<const SMDS_MeshNode*> nodesRecur;
7418           nodesRecur.insert(n);
7419           while (!stopRecur) {
7420             TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( n );
7421             if ( nnIt_i != nodeNodeMap.end() ) { // n sticks
7422               n = (*nnIt_i).second;
7423               if (!nodesRecur.insert(n).second) {
7424                 // error: recursive dependency
7425                 stopRecur = true;
7426               }
7427             }
7428             else
7429               stopRecur = true;
7430           }
7431         } ////////// BUG 0020185: end
7432       }
7433       curNodes[ iCur ] = n;
7434       bool isUnique = nodeSet.insert( n ).second;
7435       if ( isUnique )
7436         uniqueNodes[ iUnique++ ] = n;
7437       else
7438         iRepl[ nbRepl++ ] = iCur;
7439       iCur++;
7440     }
7441
7442     // Analyse element topology after replacement
7443
7444     bool isOk = true;
7445     int nbUniqueNodes = nodeSet.size();
7446     if ( nbNodes != nbUniqueNodes ) // some nodes stick
7447     {
7448       if ( elem->IsPoly() ) // Polygons and Polyhedral volumes
7449       {
7450         if ( elem->GetType() == SMDSAbs_Face ) // Polygon
7451         {
7452           elemType.Init( elem );
7453           const bool isQuad = elemType.myIsQuad;
7454           if ( isQuad )
7455             SMDS_MeshCell::applyInterlace // interlace medium and corner nodes
7456               ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon, nbNodes ), curNodes );
7457
7458           // a polygon can divide into several elements
7459           vector<const SMDS_MeshNode *> polygons_nodes;
7460           vector<int> quantities;
7461           int nbNew = SimplifyFace( curNodes, polygons_nodes, quantities );
7462           if (nbNew > 0)
7463           {
7464             vector<const SMDS_MeshNode *> face_nodes;
7465             int inode = 0;
7466             for (int iface = 0; iface < nbNew; iface++)
7467             {
7468               int nbNewNodes = quantities[iface];
7469               face_nodes.assign( polygons_nodes.begin() + inode,
7470                                  polygons_nodes.begin() + inode + nbNewNodes );
7471               inode += nbNewNodes;
7472               if ( isQuad ) // check if a result elem is a valid quadratic polygon
7473               {
7474                 bool isValid = ( nbNewNodes % 2 == 0 );
7475                 for ( int i = 0; i < nbNewNodes && isValid; ++i )
7476                   isValid = ( elem->IsMediumNode( face_nodes[i]) == bool( i % 2 ));
7477                 elemType.SetQuad( isValid );
7478                 if ( isValid ) // put medium nodes after corners
7479                   SMDS_MeshCell::applyInterlaceRev
7480                     ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon,
7481                                                           nbNewNodes ), face_nodes );
7482               }
7483               elemType.SetPoly(( nbNewNodes / ( elemType.myIsQuad + 1 ) > 4 ));
7484
7485               SMDS_MeshElement* newElem = AddElement( face_nodes, elemType.SetID(-1));
7486               if ( aShapeId )
7487                 aMesh->SetMeshElementOnShape(newElem, aShapeId);
7488             }
7489           }
7490           rmElemIds.push_back(elem->GetID());
7491
7492         } // Polygon
7493
7494         else if ( elem->GetType() == SMDSAbs_Volume ) // Polyhedral volume
7495         {
7496           if ( nbUniqueNodes < 4 ) {
7497             rmElemIds.push_back(elem->GetID());
7498           }
7499           else {
7500             // each face has to be analyzed in order to check volume validity
7501             const SMDS_VtkVolume* aPolyedre = dynamic_cast<const SMDS_VtkVolume*>( elem );
7502             if ( aPolyedre )
7503             {
7504               int nbFaces = aPolyedre->NbFaces();
7505
7506               vector<const SMDS_MeshNode *> poly_nodes;
7507               vector<int>                   quantities;
7508               vector<const SMDS_MeshNode *> faceNodes;
7509
7510               for (int iface = 1; iface <= nbFaces; iface++)
7511               {
7512                 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7513                 faceNodes.resize( nbFaceNodes );
7514                 for (int inode = 1; inode <= nbFaceNodes; inode++)
7515                 {
7516                   const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7517                   TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7518                   if ( nnIt != nodeNodeMap.end() ) // faceNode sticks
7519                     faceNode = (*nnIt).second;
7520                   faceNodes[inode - 1] = faceNode;
7521                 }
7522                 SimplifyFace(faceNodes, poly_nodes, quantities);
7523               }
7524
7525               if ( quantities.size() > 3 ) {
7526                 // TODO: remove coincident faces
7527               }
7528
7529               if ( quantities.size() > 3 )
7530               {
7531                 const SMDS_MeshElement* newElem =
7532                   aMesh->AddPolyhedralVolume( poly_nodes, quantities );
7533                 myLastCreatedElems.Append( newElem );
7534                 if ( aShapeId && newElem )
7535                   aMesh->SetMeshElementOnShape( newElem, aShapeId );
7536                 rmElemIds.push_back( elem->GetID() );
7537               }
7538             }
7539             else {
7540               rmElemIds.push_back( elem->GetID() );
7541             }
7542           }
7543         }
7544         else {
7545         }
7546
7547         continue;
7548       } // poly element
7549
7550       // Regular elements
7551       // TODO not all the possible cases are solved. Find something more generic?
7552       switch ( entity ) {
7553       case SMDSEntity_Edge: //////// EDGE
7554       case SMDSEntity_Triangle: //// TRIANGLE
7555       case SMDSEntity_Quad_Triangle:
7556       case SMDSEntity_Tetra:
7557       case SMDSEntity_Quad_Tetra: // TETRAHEDRON
7558       {
7559         isOk = false;
7560         break;
7561       }
7562       case SMDSEntity_Quad_Edge:
7563       {
7564         isOk = false; // to linear EDGE ???????
7565         break;
7566       }
7567       case SMDSEntity_Quadrangle: //////////////////////////////////// QUADRANGLE
7568       {
7569         if ( nbUniqueNodes < 3 )
7570           isOk = false;
7571         else if ( nbRepl == 1 && curNodes[ iRepl[0]] == curNodes[( iRepl[0]+2 )%4 ])
7572           isOk = false; // opposite nodes stick
7573         break;
7574       }
7575       case SMDSEntity_Quad_Quadrangle: // Quadratic QUADRANGLE
7576       {
7577         //   1    5    2
7578         //    +---+---+
7579         //    |       |
7580         //   4+       +6
7581         //    |       |
7582         //    +---+---+
7583         //   0    7    3
7584         if (( nbUniqueNodes == 6 && nbRepl == 2 ) &&
7585             (( iRepl[0] == 1 && iRepl[1] == 4 && curNodes[1] == curNodes[0] ) ||
7586              ( iRepl[0] == 2 && iRepl[1] == 5 && curNodes[2] == curNodes[1] ) ||
7587              ( iRepl[0] == 3 && iRepl[1] == 6 && curNodes[3] == curNodes[2] ) ||
7588              ( iRepl[0] == 3 && iRepl[1] == 7 && curNodes[3] == curNodes[0] )))
7589         {
7590           isOk = true;
7591         }
7592         break;
7593       }
7594       case SMDSEntity_BiQuad_Quadrangle: // Bi-Quadratic QUADRANGLE
7595       {
7596         //   1    5    2
7597         //    +---+---+
7598         //    |       |
7599         //   4+  8+   +6
7600         //    |       |
7601         //    +---+---+
7602         //   0    7    3
7603         if (( nbUniqueNodes == 7 && nbRepl == 2 && iRepl[1] != 8 ) &&
7604             (( iRepl[0] == 1 && iRepl[1] == 4 && curNodes[1] == curNodes[0] ) ||
7605              ( iRepl[0] == 2 && iRepl[1] == 5 && curNodes[2] == curNodes[1] ) ||
7606              ( iRepl[0] == 3 && iRepl[1] == 6 && curNodes[3] == curNodes[2] ) ||
7607              ( iRepl[0] == 3 && iRepl[1] == 7 && curNodes[3] == curNodes[0] )))
7608         {
7609           isOk = true;
7610         }
7611         break;
7612       }
7613       case SMDSEntity_Penta: ///////////////////////////////////// PENTAHEDRON
7614       {
7615         isOk = false;
7616         if ( nbUniqueNodes == 4 ) {
7617           // ---------------------------------> tetrahedron
7618           if ( curNodes[3] == curNodes[4] &&
7619                curNodes[3] == curNodes[5] ) {
7620             // top nodes stick
7621             isOk = true;
7622           }
7623           else if ( curNodes[0] == curNodes[1] &&
7624                     curNodes[0] == curNodes[2] ) {
7625             // bottom nodes stick: set a top before
7626             uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7627             uniqueNodes[ 0 ] = curNodes [ 5 ];
7628             uniqueNodes[ 1 ] = curNodes [ 4 ];
7629             uniqueNodes[ 2 ] = curNodes [ 3 ];
7630             isOk = true;
7631           }
7632           else if (( curNodes[0] == curNodes[3] ) +
7633                    ( curNodes[1] == curNodes[4] ) +
7634                    ( curNodes[2] == curNodes[5] ) == 2 ) {
7635             // a lateral face turns into a line
7636             isOk = true;
7637           }
7638         }
7639         else if ( nbUniqueNodes == 5 ) {
7640           // PENTAHEDRON --------------------> pyramid
7641           if ( curNodes[0] == curNodes[3] )
7642           {
7643             uniqueNodes[ 0 ] = curNodes[ 1 ];
7644             uniqueNodes[ 1 ] = curNodes[ 4 ];
7645             uniqueNodes[ 2 ] = curNodes[ 5 ];
7646             uniqueNodes[ 3 ] = curNodes[ 2 ];
7647             uniqueNodes[ 4 ] = curNodes[ 0 ];
7648             isOk = true;
7649           }
7650           if ( curNodes[1] == curNodes[4] )
7651           {
7652             uniqueNodes[ 0 ] = curNodes[ 0 ];
7653             uniqueNodes[ 1 ] = curNodes[ 2 ];
7654             uniqueNodes[ 2 ] = curNodes[ 5 ];
7655             uniqueNodes[ 3 ] = curNodes[ 3 ];
7656             uniqueNodes[ 4 ] = curNodes[ 1 ];
7657             isOk = true;
7658           }
7659           if ( curNodes[2] == curNodes[5] )
7660           {
7661             uniqueNodes[ 0 ] = curNodes[ 0 ];
7662             uniqueNodes[ 1 ] = curNodes[ 3 ];
7663             uniqueNodes[ 2 ] = curNodes[ 4 ];
7664             uniqueNodes[ 3 ] = curNodes[ 1 ];
7665             uniqueNodes[ 4 ] = curNodes[ 2 ];
7666             isOk = true;
7667           }
7668         }
7669         break;
7670       }
7671       case SMDSEntity_Hexa:
7672       {
7673         //////////////////////////////////// HEXAHEDRON
7674         isOk = false;
7675         SMDS_VolumeTool hexa (elem);
7676         hexa.SetExternalNormal();
7677         if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7678           //////////////////////// HEX ---> tetrahedron
7679           for ( int iFace = 0; iFace < 6; iFace++ ) {
7680             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7681             if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7682                 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7683                 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7684               // one face turns into a point ...
7685               int  pickInd = ind[ 0 ];
7686               int iOppFace = hexa.GetOppFaceIndex( iFace );
7687               ind = hexa.GetFaceNodesIndices( iOppFace );
7688               int nbStick = 0;
7689               uniqueNodes.clear();
7690               for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7691                 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7692                   nbStick++;
7693                 else
7694                   uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7695               }
7696               if ( nbStick == 1 ) {
7697                 // ... and the opposite one - into a triangle.
7698                 // set a top node
7699                 uniqueNodes.push_back( curNodes[ pickInd ]);
7700                 isOk = true;
7701               }
7702               break;
7703             }
7704           }
7705         }
7706         else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7707           //////////////////////// HEX ---> prism
7708           int nbTria = 0, iTria[3];
7709           const int *ind; // indices of face nodes
7710           // look for triangular faces
7711           for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7712             ind = hexa.GetFaceNodesIndices( iFace );
7713             TIDSortedNodeSet faceNodes;
7714             for ( iCur = 0; iCur < 4; iCur++ )
7715               faceNodes.insert( curNodes[ind[iCur]] );
7716             if ( faceNodes.size() == 3 )
7717               iTria[ nbTria++ ] = iFace;
7718           }
7719           // check if triangles are opposite
7720           if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7721           {
7722             // set nodes of the bottom triangle
7723             ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7724             vector<int> indB;
7725             for ( iCur = 0; iCur < 4; iCur++ )
7726               if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7727                 indB.push_back( ind[iCur] );
7728             if ( !hexa.IsForward() )
7729               std::swap( indB[0], indB[2] );
7730             for ( iCur = 0; iCur < 3; iCur++ )
7731               uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7732             // set nodes of the top triangle
7733             const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7734             for ( iCur = 0; iCur < 3; ++iCur )
7735               for ( int j = 0; j < 4; ++j )
7736                 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7737                 {
7738                   uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7739                   break;
7740                 }
7741             isOk = true;
7742             break;
7743           }
7744         }
7745         else if (nbUniqueNodes == 5 && nbRepl == 3 ) {
7746           //////////////////// HEXAHEDRON ---> pyramid
7747           for ( int iFace = 0; iFace < 6; iFace++ ) {
7748             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7749             if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7750                 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7751                 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7752               // one face turns into a point ...
7753               int iOppFace = hexa.GetOppFaceIndex( iFace );
7754               ind = hexa.GetFaceNodesIndices( iOppFace );
7755               uniqueNodes.clear();
7756               for ( iCur = 0; iCur < 4; iCur++ ) {
7757                 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7758                   break;
7759                 else
7760                   uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7761               }
7762               if ( uniqueNodes.size() == 4 ) {
7763                 // ... and the opposite one is a quadrangle
7764                 // set a top node
7765                 const int* indTop = hexa.GetFaceNodesIndices( iFace );
7766                 uniqueNodes.push_back( curNodes[indTop[ 0 ]]);
7767                 isOk = true;
7768               }
7769               break;
7770             }
7771           }
7772         }
7773
7774         if ( !isOk && nbUniqueNodes > 4 ) {
7775           ////////////////// HEXAHEDRON ---> polyhedron
7776           hexa.SetExternalNormal();
7777           vector<const SMDS_MeshNode *> poly_nodes; poly_nodes.reserve( 6 * 4 );
7778           vector<int>                   quantities; quantities.reserve( 6 );
7779           for ( int iFace = 0; iFace < 6; iFace++ )
7780           {
7781             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7782             if ( curNodes[ind[0]] == curNodes[ind[2]] ||
7783                  curNodes[ind[1]] == curNodes[ind[3]] )
7784             {
7785               quantities.clear();
7786               break; // opposite nodes stick
7787             }
7788             nodeSet.clear();
7789             for ( iCur = 0; iCur < 4; iCur++ )
7790             {
7791               if ( nodeSet.insert( curNodes[ind[ iCur ]] ).second )
7792                 poly_nodes.push_back( curNodes[ind[ iCur ]]);
7793             }
7794             if ( nodeSet.size() < 3 )
7795               poly_nodes.resize( poly_nodes.size() - nodeSet.size() );
7796             else
7797               quantities.push_back( nodeSet.size() );
7798           }
7799           if ( quantities.size() >= 4 )
7800           {
7801             const SMDS_MeshElement* newElem = aMesh->AddPolyhedralVolume( poly_nodes, quantities );
7802             myLastCreatedElems.Append( newElem );
7803             if ( aShapeId && newElem )
7804               aMesh->SetMeshElementOnShape( newElem, aShapeId );
7805             rmElemIds.push_back( elem->GetID() );
7806           }
7807         }
7808         break;
7809       } // case HEXAHEDRON
7810
7811       default:
7812         isOk = false;
7813       } // switch ( nbNodes )
7814
7815     } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7816
7817     if ( isOk ) // a non-poly elem remains valid after sticking nodes
7818     {
7819       if ( nbNodes != nbUniqueNodes ||
7820            !aMesh->ChangeElementNodes( elem, & curNodes[0], nbNodes ))
7821       {
7822         elemType.Init( elem ).SetID( elem->GetID() );
7823
7824         SMESHDS_SubMesh * sm = aShapeId > 0 ? aMesh->MeshElements(aShapeId) : 0;
7825         aMesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
7826
7827         uniqueNodes.resize(nbUniqueNodes);
7828         SMDS_MeshElement* newElem = this->AddElement( uniqueNodes, elemType );
7829         if ( sm && newElem )
7830           sm->AddElement( newElem );
7831         if ( elem != newElem )
7832           ReplaceElemInGroups( elem, newElem, aMesh );
7833       }
7834     }
7835     else {
7836       // Remove invalid regular element or invalid polygon
7837       rmElemIds.push_back( elem->GetID() );
7838     }
7839
7840   } // loop on elements
7841
7842   // Remove bad elements, then equal nodes (order important)
7843
7844   Remove( rmElemIds, false );
7845   Remove( rmNodeIds, true );
7846
7847   return;
7848 }
7849
7850
7851 // ========================================================
7852 // class   : SortableElement
7853 // purpose : allow sorting elements basing on their nodes
7854 // ========================================================
7855 class SortableElement : public set <const SMDS_MeshElement*>
7856 {
7857 public:
7858
7859   SortableElement( const SMDS_MeshElement* theElem )
7860   {
7861     myElem = theElem;
7862     SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
7863     while ( nodeIt->more() )
7864       this->insert( nodeIt->next() );
7865   }
7866
7867   const SMDS_MeshElement* Get() const
7868   { return myElem; }
7869
7870 private:
7871   mutable const SMDS_MeshElement* myElem;
7872 };
7873
7874 //=======================================================================
7875 //function : FindEqualElements
7876 //purpose  : Return list of group of elements built on the same nodes.
7877 //           Search among theElements or in the whole mesh if theElements is empty
7878 //=======================================================================
7879
7880 void SMESH_MeshEditor::FindEqualElements(TIDSortedElemSet &        theElements,
7881                                          TListOfListOfElementsID & theGroupsOfElementsID)
7882 {
7883   myLastCreatedElems.Clear();
7884   myLastCreatedNodes.Clear();
7885
7886   typedef map< SortableElement, int > TMapOfNodeSet;
7887   typedef list<int> TGroupOfElems;
7888
7889   if ( theElements.empty() )
7890   { // get all elements in the mesh
7891     SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator();
7892     while ( eIt->more() )
7893       theElements.insert( theElements.end(), eIt->next() );
7894   }
7895
7896   vector< TGroupOfElems > arrayOfGroups;
7897   TGroupOfElems groupOfElems;
7898   TMapOfNodeSet mapOfNodeSet;
7899
7900   TIDSortedElemSet::iterator elemIt = theElements.begin();
7901   for ( int i = 0; elemIt != theElements.end(); ++elemIt )
7902   {
7903     const SMDS_MeshElement* curElem = *elemIt;
7904     SortableElement SE(curElem);
7905     // check uniqueness
7906     pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i));
7907     if ( !pp.second ) { // one more coincident elem
7908       TMapOfNodeSet::iterator& itSE = pp.first;
7909       int ind = (*itSE).second;
7910       arrayOfGroups[ind].push_back( curElem->GetID() );
7911     }
7912     else {
7913       arrayOfGroups.push_back( groupOfElems );
7914       arrayOfGroups.back().push_back( curElem->GetID() );
7915       i++;
7916     }
7917   }
7918
7919   groupOfElems.clear();
7920   vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
7921   for ( ; groupIt != arrayOfGroups.end(); ++groupIt )
7922   {
7923     if ( groupIt->size() > 1 ) {
7924       //groupOfElems.sort(); -- theElements is sorted already
7925       theGroupsOfElementsID.push_back( groupOfElems );
7926       theGroupsOfElementsID.back().splice( theGroupsOfElementsID.back().end(), *groupIt );
7927     }
7928   }
7929 }
7930
7931 //=======================================================================
7932 //function : MergeElements
7933 //purpose  : In each given group, substitute all elements by the first one.
7934 //=======================================================================
7935
7936 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
7937 {
7938   myLastCreatedElems.Clear();
7939   myLastCreatedNodes.Clear();
7940
7941   typedef list<int> TListOfIDs;
7942   TListOfIDs rmElemIds; // IDs of elems to remove
7943
7944   SMESHDS_Mesh* aMesh = GetMeshDS();
7945
7946   TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
7947   while ( groupsIt != theGroupsOfElementsID.end() ) {
7948     TListOfIDs& aGroupOfElemID = *groupsIt;
7949     aGroupOfElemID.sort();
7950     int elemIDToKeep = aGroupOfElemID.front();
7951     const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
7952     aGroupOfElemID.pop_front();
7953     TListOfIDs::iterator idIt = aGroupOfElemID.begin();
7954     while ( idIt != aGroupOfElemID.end() ) {
7955       int elemIDToRemove = *idIt;
7956       const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
7957       // add the kept element in groups of removed one (PAL15188)
7958       AddToSameGroups( elemToKeep, elemToRemove, aMesh );
7959       rmElemIds.push_back( elemIDToRemove );
7960       ++idIt;
7961     }
7962     ++groupsIt;
7963   }
7964
7965   Remove( rmElemIds, false );
7966 }
7967
7968 //=======================================================================
7969 //function : MergeEqualElements
7970 //purpose  : Remove all but one of elements built on the same nodes.
7971 //=======================================================================
7972
7973 void SMESH_MeshEditor::MergeEqualElements()
7974 {
7975   TIDSortedElemSet aMeshElements; /* empty input ==
7976                                      to merge equal elements in the whole mesh */
7977   TListOfListOfElementsID aGroupsOfElementsID;
7978   FindEqualElements(aMeshElements, aGroupsOfElementsID);
7979   MergeElements(aGroupsOfElementsID);
7980 }
7981
7982 //=======================================================================
7983 //function : findAdjacentFace
7984 //purpose  :
7985 //=======================================================================
7986
7987 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
7988                                                 const SMDS_MeshNode* n2,
7989                                                 const SMDS_MeshElement* elem)
7990 {
7991   TIDSortedElemSet elemSet, avoidSet;
7992   if ( elem )
7993     avoidSet.insert ( elem );
7994   return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
7995 }
7996
7997 //=======================================================================
7998 //function : findSegment
7999 //purpose  : Return a mesh segment by two nodes one of which can be medium
8000 //=======================================================================
8001
8002 static const SMDS_MeshElement* findSegment(const SMDS_MeshNode* n1,
8003                                            const SMDS_MeshNode* n2)
8004 {
8005   SMDS_ElemIteratorPtr it = n1->GetInverseElementIterator( SMDSAbs_Edge );
8006   while ( it->more() )
8007   {
8008     const SMDS_MeshElement* seg = it->next();
8009     if ( seg->GetNodeIndex( n2 ) >= 0 )
8010       return seg;
8011   }
8012   return 0;
8013 }
8014
8015 //=======================================================================
8016 //function : FindFreeBorder
8017 //purpose  :
8018 //=======================================================================
8019
8020 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
8021
8022 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode*             theFirstNode,
8023                                        const SMDS_MeshNode*             theSecondNode,
8024                                        const SMDS_MeshNode*             theLastNode,
8025                                        list< const SMDS_MeshNode* > &   theNodes,
8026                                        list< const SMDS_MeshElement* >& theFaces)
8027 {
8028   if ( !theFirstNode || !theSecondNode )
8029     return false;
8030   // find border face between theFirstNode and theSecondNode
8031   const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
8032   if ( !curElem )
8033     return false;
8034
8035   theFaces.push_back( curElem );
8036   theNodes.push_back( theFirstNode );
8037   theNodes.push_back( theSecondNode );
8038
8039   const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
8040   TIDSortedElemSet foundElems;
8041   bool needTheLast = ( theLastNode != 0 );
8042
8043   while ( nStart != theLastNode ) {
8044     if ( nStart == theFirstNode )
8045       return !needTheLast;
8046
8047     // find all free border faces sharing form nStart
8048
8049     list< const SMDS_MeshElement* > curElemList;
8050     list< const SMDS_MeshNode* >    nStartList;
8051     SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
8052     while ( invElemIt->more() ) {
8053       const SMDS_MeshElement* e = invElemIt->next();
8054       if ( e == curElem || foundElems.insert( e ).second ) {
8055         // get nodes
8056         int iNode = 0, nbNodes = e->NbNodes();
8057         vector<const SMDS_MeshNode*> nodes(nbNodes+1);
8058
8059         if ( e->IsQuadratic() ) {
8060           const SMDS_VtkFace* F =
8061             dynamic_cast<const SMDS_VtkFace*>(e);
8062           if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8063           // use special nodes iterator
8064           SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8065           while( anIter->more() ) {
8066             nodes[ iNode++ ] = cast2Node(anIter->next());
8067           }
8068         }
8069         else {
8070           SMDS_ElemIteratorPtr nIt = e->nodesIterator();
8071           while ( nIt->more() )
8072             nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
8073         }
8074         nodes[ iNode ] = nodes[ 0 ];
8075         // check 2 links
8076         for ( iNode = 0; iNode < nbNodes; iNode++ )
8077           if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
8078                (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
8079               ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
8080           {
8081             nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
8082             curElemList.push_back( e );
8083           }
8084       }
8085     }
8086     // analyse the found
8087
8088     int nbNewBorders = curElemList.size();
8089     if ( nbNewBorders == 0 ) {
8090       // no free border furthermore
8091       return !needTheLast;
8092     }
8093     else if ( nbNewBorders == 1 ) {
8094       // one more element found
8095       nIgnore = nStart;
8096       nStart = nStartList.front();
8097       curElem = curElemList.front();
8098       theFaces.push_back( curElem );
8099       theNodes.push_back( nStart );
8100     }
8101     else {
8102       // several continuations found
8103       list< const SMDS_MeshElement* >::iterator curElemIt;
8104       list< const SMDS_MeshNode* >::iterator nStartIt;
8105       // check if one of them reached the last node
8106       if ( needTheLast ) {
8107         for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8108              curElemIt!= curElemList.end();
8109              curElemIt++, nStartIt++ )
8110           if ( *nStartIt == theLastNode ) {
8111             theFaces.push_back( *curElemIt );
8112             theNodes.push_back( *nStartIt );
8113             return true;
8114           }
8115       }
8116       // find the best free border by the continuations
8117       list<const SMDS_MeshNode*>    contNodes[ 2 ], *cNL;
8118       list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
8119       for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8120            curElemIt!= curElemList.end();
8121            curElemIt++, nStartIt++ )
8122       {
8123         cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
8124         cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
8125         // find one more free border
8126         if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
8127           cNL->clear();
8128           cFL->clear();
8129         }
8130         else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
8131           // choice: clear a worse one
8132           int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
8133           int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
8134           contNodes[ iWorse ].clear();
8135           contFaces[ iWorse ].clear();
8136         }
8137       }
8138       if ( contNodes[0].empty() && contNodes[1].empty() )
8139         return false;
8140
8141       // append the best free border
8142       cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
8143       cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8144       theNodes.pop_back(); // remove nIgnore
8145       theNodes.pop_back(); // remove nStart
8146       theFaces.pop_back(); // remove curElem
8147       list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
8148       list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
8149       for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
8150       for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
8151       return true;
8152
8153     } // several continuations found
8154   } // while ( nStart != theLastNode )
8155
8156   return true;
8157 }
8158
8159 //=======================================================================
8160 //function : CheckFreeBorderNodes
8161 //purpose  : Return true if the tree nodes are on a free border
8162 //=======================================================================
8163
8164 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8165                                             const SMDS_MeshNode* theNode2,
8166                                             const SMDS_MeshNode* theNode3)
8167 {
8168   list< const SMDS_MeshNode* > nodes;
8169   list< const SMDS_MeshElement* > faces;
8170   return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8171 }
8172
8173 //=======================================================================
8174 //function : SewFreeBorder
8175 //purpose  :
8176 //warning  : for border-to-side sewing theSideSecondNode is considered as
8177 //           the last side node and theSideThirdNode is not used
8178 //=======================================================================
8179
8180 SMESH_MeshEditor::Sew_Error
8181 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8182                                  const SMDS_MeshNode* theBordSecondNode,
8183                                  const SMDS_MeshNode* theBordLastNode,
8184                                  const SMDS_MeshNode* theSideFirstNode,
8185                                  const SMDS_MeshNode* theSideSecondNode,
8186                                  const SMDS_MeshNode* theSideThirdNode,
8187                                  const bool           theSideIsFreeBorder,
8188                                  const bool           toCreatePolygons,
8189                                  const bool           toCreatePolyedrs)
8190 {
8191   myLastCreatedElems.Clear();
8192   myLastCreatedNodes.Clear();
8193
8194   Sew_Error aResult = SEW_OK;
8195
8196   // ====================================
8197   //    find side nodes and elements
8198   // ====================================
8199
8200   list< const SMDS_MeshNode* >    nSide[ 2 ];
8201   list< const SMDS_MeshElement* > eSide[ 2 ];
8202   list< const SMDS_MeshNode* >::iterator    nIt[ 2 ];
8203   list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8204
8205   // Free border 1
8206   // --------------
8207   if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8208                       nSide[0], eSide[0])) {
8209     MESSAGE(" Free Border 1 not found " );
8210     aResult = SEW_BORDER1_NOT_FOUND;
8211   }
8212   if (theSideIsFreeBorder) {
8213     // Free border 2
8214     // --------------
8215     if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8216                         nSide[1], eSide[1])) {
8217       MESSAGE(" Free Border 2 not found " );
8218       aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8219     }
8220   }
8221   if ( aResult != SEW_OK )
8222     return aResult;
8223
8224   if (!theSideIsFreeBorder) {
8225     // Side 2
8226     // --------------
8227
8228     // -------------------------------------------------------------------------
8229     // Algo:
8230     // 1. If nodes to merge are not coincident, move nodes of the free border
8231     //    from the coord sys defined by the direction from the first to last
8232     //    nodes of the border to the correspondent sys of the side 2
8233     // 2. On the side 2, find the links most co-directed with the correspondent
8234     //    links of the free border
8235     // -------------------------------------------------------------------------
8236
8237     // 1. Since sewing may break if there are volumes to split on the side 2,
8238     //    we wont move nodes but just compute new coordinates for them
8239     typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8240     TNodeXYZMap nBordXYZ;
8241     list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8242     list< const SMDS_MeshNode* >::iterator nBordIt;
8243
8244     gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8245     gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8246     gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8247     gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8248     double tol2 = 1.e-8;
8249     gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8250     if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8251       // Need node movement.
8252
8253       // find X and Z axes to create trsf
8254       gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8255       gp_Vec X = Zs ^ Zb;
8256       if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8257         // Zb || Zs
8258         X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8259
8260       // coord systems
8261       gp_Ax3 toBordAx( Pb1, Zb, X );
8262       gp_Ax3 fromSideAx( Ps1, Zs, X );
8263       gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8264       // set trsf
8265       gp_Trsf toBordSys, fromSide2Sys;
8266       toBordSys.SetTransformation( toBordAx );
8267       fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8268       fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8269
8270       // move
8271       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8272         const SMDS_MeshNode* n = *nBordIt;
8273         gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8274         toBordSys.Transforms( xyz );
8275         fromSide2Sys.Transforms( xyz );
8276         nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8277       }
8278     }
8279     else {
8280       // just insert nodes XYZ in the nBordXYZ map
8281       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8282         const SMDS_MeshNode* n = *nBordIt;
8283         nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8284       }
8285     }
8286
8287     // 2. On the side 2, find the links most co-directed with the correspondent
8288     //    links of the free border
8289
8290     list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8291     list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8292     sideNodes.push_back( theSideFirstNode );
8293
8294     bool hasVolumes = false;
8295     LinkID_Gen aLinkID_Gen( GetMeshDS() );
8296     set<long> foundSideLinkIDs, checkedLinkIDs;
8297     SMDS_VolumeTool volume;
8298     //const SMDS_MeshNode* faceNodes[ 4 ];
8299
8300     const SMDS_MeshNode*    sideNode;
8301     const SMDS_MeshElement* sideElem  = 0;
8302     const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8303     const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8304     nBordIt = bordNodes.begin();
8305     nBordIt++;
8306     // border node position and border link direction to compare with
8307     gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8308     gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8309     // choose next side node by link direction or by closeness to
8310     // the current border node:
8311     bool searchByDir = ( *nBordIt != theBordLastNode );
8312     do {
8313       // find the next node on the Side 2
8314       sideNode = 0;
8315       double maxDot = -DBL_MAX, minDist = DBL_MAX;
8316       long linkID;
8317       checkedLinkIDs.clear();
8318       gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8319
8320       // loop on inverse elements of current node (prevSideNode) on the Side 2
8321       SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8322       while ( invElemIt->more() )
8323       {
8324         const SMDS_MeshElement* elem = invElemIt->next();
8325         // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8326         int iPrevNode = 0, iNode = 0, nbNodes = elem->NbNodes();
8327         vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8328         bool isVolume = volume.Set( elem );
8329         const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8330         if ( isVolume ) // --volume
8331           hasVolumes = true;
8332         else if ( elem->GetType()==SMDSAbs_Face ) { // --face
8333           // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8334           if(elem->IsQuadratic()) {
8335             const SMDS_VtkFace* F =
8336               dynamic_cast<const SMDS_VtkFace*>(elem);
8337             if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8338             // use special nodes iterator
8339             SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8340             while( anIter->more() ) {
8341               nodes[ iNode ] = cast2Node(anIter->next());
8342               if ( nodes[ iNode++ ] == prevSideNode )
8343                 iPrevNode = iNode - 1;
8344             }
8345           }
8346           else {
8347             SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
8348             while ( nIt->more() ) {
8349               nodes[ iNode ] = cast2Node( nIt->next() );
8350               if ( nodes[ iNode++ ] == prevSideNode )
8351                 iPrevNode = iNode - 1;
8352             }
8353           }
8354           // there are 2 links to check
8355           nbNodes = 2;
8356         }
8357         else // --edge
8358           continue;
8359         // loop on links, to be precise, on the second node of links
8360         for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8361           const SMDS_MeshNode* n = nodes[ iNode ];
8362           if ( isVolume ) {
8363             if ( !volume.IsLinked( n, prevSideNode ))
8364               continue;
8365           }
8366           else {
8367             if ( iNode ) // a node before prevSideNode
8368               n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8369             else         // a node after prevSideNode
8370               n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8371           }
8372           // check if this link was already used
8373           long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8374           bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8375           if (!isJustChecked &&
8376               foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8377           {
8378             // test a link geometrically
8379             gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8380             bool linkIsBetter = false;
8381             double dot = 0.0, dist = 0.0;
8382             if ( searchByDir ) { // choose most co-directed link
8383               dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8384               linkIsBetter = ( dot > maxDot );
8385             }
8386             else { // choose link with the node closest to bordPos
8387               dist = ( nextXYZ - bordPos ).SquareModulus();
8388               linkIsBetter = ( dist < minDist );
8389             }
8390             if ( linkIsBetter ) {
8391               maxDot = dot;
8392               minDist = dist;
8393               linkID = iLink;
8394               sideNode = n;
8395               sideElem = elem;
8396             }
8397           }
8398         }
8399       } // loop on inverse elements of prevSideNode
8400
8401       if ( !sideNode ) {
8402         MESSAGE(" Cant find path by links of the Side 2 ");
8403         return SEW_BAD_SIDE_NODES;
8404       }
8405       sideNodes.push_back( sideNode );
8406       sideElems.push_back( sideElem );
8407       foundSideLinkIDs.insert ( linkID );
8408       prevSideNode = sideNode;
8409
8410       if ( *nBordIt == theBordLastNode )
8411         searchByDir = false;
8412       else {
8413         // find the next border link to compare with
8414         gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8415         searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8416         // move to next border node if sideNode is before forward border node (bordPos)
8417         while ( *nBordIt != theBordLastNode && !searchByDir ) {
8418           prevBordNode = *nBordIt;
8419           nBordIt++;
8420           bordPos = nBordXYZ[ *nBordIt ];
8421           bordDir = bordPos - nBordXYZ[ prevBordNode ];
8422           searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8423         }
8424       }
8425     }
8426     while ( sideNode != theSideSecondNode );
8427
8428     if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8429       MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8430       return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8431     }
8432   } // end nodes search on the side 2
8433
8434   // ============================
8435   // sew the border to the side 2
8436   // ============================
8437
8438   int nbNodes[]  = { (int)nSide[0].size(), (int)nSide[1].size() };
8439   int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8440
8441   bool toMergeConformal = ( nbNodes[0] == nbNodes[1] );
8442   if ( toMergeConformal && toCreatePolygons )
8443   {
8444     // do not merge quadrangles if polygons are OK (IPAL0052824)
8445     eIt[0] = eSide[0].begin();
8446     eIt[1] = eSide[1].begin();
8447     bool allQuads[2] = { true, true };
8448     for ( int iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8449       for ( ; allQuads[iBord] && eIt[iBord] != eSide[iBord].end(); ++eIt[iBord] )
8450         allQuads[iBord] = ( (*eIt[iBord])->NbCornerNodes() == 4 );
8451     }
8452     toMergeConformal = ( !allQuads[0] && !allQuads[1] );
8453   }
8454
8455   TListOfListOfNodes nodeGroupsToMerge;
8456   if (( toMergeConformal ) ||
8457       ( theSideIsFreeBorder && !theSideThirdNode )) {
8458
8459     // all nodes are to be merged
8460
8461     for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8462          nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8463          nIt[0]++, nIt[1]++ )
8464     {
8465       nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8466       nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8467       nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8468     }
8469   }
8470   else {
8471
8472     // insert new nodes into the border and the side to get equal nb of segments
8473
8474     // get normalized parameters of nodes on the borders
8475     vector< double > param[ 2 ];
8476     param[0].resize( maxNbNodes );
8477     param[1].resize( maxNbNodes );
8478     int iNode, iBord;
8479     for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8480       list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8481       list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8482       const SMDS_MeshNode* nPrev = *nIt;
8483       double bordLength = 0;
8484       for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8485         const SMDS_MeshNode* nCur = *nIt;
8486         gp_XYZ segment (nCur->X() - nPrev->X(),
8487                         nCur->Y() - nPrev->Y(),
8488                         nCur->Z() - nPrev->Z());
8489         double segmentLen = segment.Modulus();
8490         bordLength += segmentLen;
8491         param[ iBord ][ iNode ] = bordLength;
8492         nPrev = nCur;
8493       }
8494       // normalize within [0,1]
8495       for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8496         param[ iBord ][ iNode ] /= bordLength;
8497       }
8498     }
8499
8500     // loop on border segments
8501     const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8502     int i[ 2 ] = { 0, 0 };
8503     nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8504     nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8505
8506     TElemOfNodeListMap insertMap;
8507     TElemOfNodeListMap::iterator insertMapIt;
8508     // insertMap is
8509     // key:   elem to insert nodes into
8510     // value: 2 nodes to insert between + nodes to be inserted
8511     do {
8512       bool next[ 2 ] = { false, false };
8513
8514       // find min adjacent segment length after sewing
8515       double nextParam = 10., prevParam = 0;
8516       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8517         if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8518           nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8519         if ( i[ iBord ] > 0 )
8520           prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8521       }
8522       double  minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8523       double  maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8524       double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8525
8526       // choose to insert or to merge nodes
8527       double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8528       if ( Abs( du ) <= minSegLen * 0.2 ) {
8529         // merge
8530         // ------
8531         nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8532         const SMDS_MeshNode* n0 = *nIt[0];
8533         const SMDS_MeshNode* n1 = *nIt[1];
8534         nodeGroupsToMerge.back().push_back( n1 );
8535         nodeGroupsToMerge.back().push_back( n0 );
8536         // position of node of the border changes due to merge
8537         param[ 0 ][ i[0] ] += du;
8538         // move n1 for the sake of elem shape evaluation during insertion.
8539         // n1 will be removed by MergeNodes() anyway
8540         const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8541         next[0] = next[1] = true;
8542       }
8543       else {
8544         // insert
8545         // ------
8546         int intoBord = ( du < 0 ) ? 0 : 1;
8547         const SMDS_MeshElement* elem = *eIt [ intoBord ];
8548         const SMDS_MeshNode*    n1   = nPrev[ intoBord ];
8549         const SMDS_MeshNode*    n2   = *nIt [ intoBord ];
8550         const SMDS_MeshNode*    nIns = *nIt [ 1 - intoBord ];
8551         if ( intoBord == 1 ) {
8552           // move node of the border to be on a link of elem of the side
8553           gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8554           gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8555           double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8556           gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8557           GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8558         }
8559         insertMapIt = insertMap.find( elem );
8560         bool  notFound = ( insertMapIt == insertMap.end() );
8561         bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8562         if ( otherLink ) {
8563           // insert into another link of the same element:
8564           // 1. perform insertion into the other link of the elem
8565           list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8566           const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8567           const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8568           InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8569           // 2. perform insertion into the link of adjacent faces
8570           while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem )) {
8571             InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8572           }
8573           while ( const SMDS_MeshElement* seg = findSegment( n12, n22 )) {
8574             InsertNodesIntoLink( seg, n12, n22, nodeList );
8575           }
8576           if (toCreatePolyedrs) {
8577             // perform insertion into the links of adjacent volumes
8578             UpdateVolumes(n12, n22, nodeList);
8579           }
8580           // 3. find an element appeared on n1 and n2 after the insertion
8581           insertMap.erase( elem );
8582           elem = findAdjacentFace( n1, n2, 0 );
8583         }
8584         if ( notFound || otherLink ) {
8585           // add element and nodes of the side into the insertMap
8586           insertMapIt = insertMap.insert( make_pair( elem, list<const SMDS_MeshNode*>() )).first;
8587           (*insertMapIt).second.push_back( n1 );
8588           (*insertMapIt).second.push_back( n2 );
8589         }
8590         // add node to be inserted into elem
8591         (*insertMapIt).second.push_back( nIns );
8592         next[ 1 - intoBord ] = true;
8593       }
8594
8595       // go to the next segment
8596       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8597         if ( next[ iBord ] ) {
8598           if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8599             eIt[ iBord ]++;
8600           nPrev[ iBord ] = *nIt[ iBord ];
8601           nIt[ iBord ]++; i[ iBord ]++;
8602         }
8603       }
8604     }
8605     while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8606
8607     // perform insertion of nodes into elements
8608
8609     for (insertMapIt = insertMap.begin();
8610          insertMapIt != insertMap.end();
8611          insertMapIt++ )
8612     {
8613       const SMDS_MeshElement* elem = (*insertMapIt).first;
8614       list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8615       const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8616       const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8617
8618       InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8619
8620       while ( const SMDS_MeshElement* seg = findSegment( n1, n2 )) {
8621         InsertNodesIntoLink( seg, n1, n2, nodeList );
8622       }
8623
8624       if ( !theSideIsFreeBorder ) {
8625         // look for and insert nodes into the faces adjacent to elem
8626         while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem )) {
8627           InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8628         }
8629       }
8630       if (toCreatePolyedrs) {
8631         // perform insertion into the links of adjacent volumes
8632         UpdateVolumes(n1, n2, nodeList);
8633       }
8634     }
8635   } // end: insert new nodes
8636
8637   MergeNodes ( nodeGroupsToMerge );
8638
8639
8640   // Remove coincident segments
8641
8642   // get new segments
8643   TIDSortedElemSet segments;
8644   SMESH_SequenceOfElemPtr newFaces;
8645   for ( int i = 1; i <= myLastCreatedElems.Length(); ++i )
8646   {
8647     if ( !myLastCreatedElems(i) ) continue;
8648     if ( myLastCreatedElems(i)->GetType() == SMDSAbs_Edge )
8649       segments.insert( segments.end(), myLastCreatedElems(i) );
8650     else
8651       newFaces.Append( myLastCreatedElems(i) );
8652   }
8653   // get segments adjacent to merged nodes
8654   TListOfListOfNodes::iterator groupIt = nodeGroupsToMerge.begin();
8655   for ( ; groupIt != nodeGroupsToMerge.end(); groupIt++ )
8656   {
8657     const list<const SMDS_MeshNode*>& nodes = *groupIt;
8658     SMDS_ElemIteratorPtr segIt = nodes.front()->GetInverseElementIterator( SMDSAbs_Edge );
8659     while ( segIt->more() )
8660       segments.insert( segIt->next() );
8661   }
8662
8663   // find coincident
8664   TListOfListOfElementsID equalGroups;
8665   if ( !segments.empty() )
8666     FindEqualElements( segments, equalGroups );
8667   if ( !equalGroups.empty() )
8668   {
8669     // remove from segments those that will be removed
8670     TListOfListOfElementsID::iterator itGroups = equalGroups.begin();
8671     for ( ; itGroups != equalGroups.end(); ++itGroups )
8672     {
8673       list< int >& group = *itGroups;
8674       list< int >::iterator id = group.begin();
8675       for ( ++id; id != group.end(); ++id )
8676         if ( const SMDS_MeshElement* seg = GetMeshDS()->FindElement( *id ))
8677           segments.erase( seg );
8678     }
8679     // remove equal segments
8680     MergeElements( equalGroups );
8681
8682     // restore myLastCreatedElems
8683     myLastCreatedElems = newFaces;
8684     TIDSortedElemSet::iterator seg = segments.begin();
8685     for ( ; seg != segments.end(); ++seg )
8686       myLastCreatedElems.Append( *seg );
8687   }
8688
8689   return aResult;
8690 }
8691
8692 //=======================================================================
8693 //function : InsertNodesIntoLink
8694 //purpose  : insert theNodesToInsert into theElement between theBetweenNode1
8695 //           and theBetweenNode2 and split theElement
8696 //=======================================================================
8697
8698 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement*     theElement,
8699                                            const SMDS_MeshNode*        theBetweenNode1,
8700                                            const SMDS_MeshNode*        theBetweenNode2,
8701                                            list<const SMDS_MeshNode*>& theNodesToInsert,
8702                                            const bool                  toCreatePoly)
8703 {
8704   if ( !theElement ) return;
8705
8706   SMESHDS_Mesh *aMesh = GetMeshDS();
8707   vector<const SMDS_MeshElement*> newElems;
8708
8709   if ( theElement->GetType() == SMDSAbs_Edge )
8710   {
8711     theNodesToInsert.push_front( theBetweenNode1 );
8712     theNodesToInsert.push_back ( theBetweenNode2 );
8713     list<const SMDS_MeshNode*>::iterator n = theNodesToInsert.begin();
8714     const SMDS_MeshNode* n1 = *n;
8715     for ( ++n; n != theNodesToInsert.end(); ++n )
8716     {
8717       const SMDS_MeshNode* n2 = *n;
8718       if ( const SMDS_MeshElement* seg = aMesh->FindEdge( n1, n2 ))
8719         AddToSameGroups( seg, theElement, aMesh );
8720       else
8721         newElems.push_back( aMesh->AddEdge ( n1, n2 ));
8722       n1 = n2;
8723     }
8724     theNodesToInsert.pop_front();
8725     theNodesToInsert.pop_back();
8726
8727     if ( theElement->IsQuadratic() ) // add a not split part
8728     {
8729       vector<const SMDS_MeshNode*> nodes( theElement->begin_nodes(),
8730                                           theElement->end_nodes() );
8731       int iOther = 0, nbN = nodes.size();
8732       for ( ; iOther < nbN; ++iOther )
8733         if ( nodes[iOther] != theBetweenNode1 &&
8734              nodes[iOther] != theBetweenNode2 )
8735           break;
8736       if      ( iOther == 0 )
8737       {
8738         if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[0], nodes[1] ))
8739           AddToSameGroups( seg, theElement, aMesh );
8740         else
8741           newElems.push_back( aMesh->AddEdge ( nodes[0], nodes[1] ));
8742       }
8743       else if ( iOther == 2 )
8744       {
8745         if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[1], nodes[2] ))
8746           AddToSameGroups( seg, theElement, aMesh );
8747         else
8748           newElems.push_back( aMesh->AddEdge ( nodes[1], nodes[2] ));
8749       }
8750     }
8751     // treat new elements
8752     for ( size_t i = 0; i < newElems.size(); ++i )
8753       if ( newElems[i] )
8754       {
8755         aMesh->SetMeshElementOnShape( newElems[i], theElement->getshapeId() );
8756         myLastCreatedElems.Append( newElems[i] );
8757       }
8758     ReplaceElemInGroups( theElement, newElems, aMesh );
8759     aMesh->RemoveElement( theElement );
8760     return;
8761
8762   } // if ( theElement->GetType() == SMDSAbs_Edge )
8763
8764   const SMDS_MeshElement* theFace = theElement;
8765   if ( theFace->GetType() != SMDSAbs_Face ) return;
8766
8767   // find indices of 2 link nodes and of the rest nodes
8768   int iNode = 0, il1, il2, i3, i4;
8769   il1 = il2 = i3 = i4 = -1;
8770   vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8771
8772   SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8773   while ( nodeIt->more() ) {
8774     const SMDS_MeshNode* n = nodeIt->next();
8775     if ( n == theBetweenNode1 )
8776       il1 = iNode;
8777     else if ( n == theBetweenNode2 )
8778       il2 = iNode;
8779     else if ( i3 < 0 )
8780       i3 = iNode;
8781     else
8782       i4 = iNode;
8783     nodes[ iNode++ ] = n;
8784   }
8785   if ( il1 < 0 || il2 < 0 || i3 < 0 )
8786     return ;
8787
8788   // arrange link nodes to go one after another regarding the face orientation
8789   bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8790   list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8791   if ( reverse ) {
8792     iNode = il1;
8793     il1 = il2;
8794     il2 = iNode;
8795     aNodesToInsert.reverse();
8796   }
8797   // check that not link nodes of a quadrangles are in good order
8798   int nbFaceNodes = theFace->NbNodes();
8799   if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8800     iNode = i3;
8801     i3 = i4;
8802     i4 = iNode;
8803   }
8804
8805   if (toCreatePoly || theFace->IsPoly()) {
8806
8807     iNode = 0;
8808     vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8809
8810     // add nodes of face up to first node of link
8811     bool isFLN = false;
8812
8813     if ( theFace->IsQuadratic() ) {
8814       const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>(theFace);
8815       if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8816       // use special nodes iterator
8817       SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8818       while( anIter->more()  && !isFLN ) {
8819         const SMDS_MeshNode* n = cast2Node(anIter->next());
8820         poly_nodes[iNode++] = n;
8821         if (n == nodes[il1]) {
8822           isFLN = true;
8823         }
8824       }
8825       // add nodes to insert
8826       list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8827       for (; nIt != aNodesToInsert.end(); nIt++) {
8828         poly_nodes[iNode++] = *nIt;
8829       }
8830       // add nodes of face starting from last node of link
8831       while ( anIter->more() ) {
8832         poly_nodes[iNode++] = cast2Node(anIter->next());
8833       }
8834     }
8835     else {
8836       SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8837       while ( nodeIt->more() && !isFLN ) {
8838         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8839         poly_nodes[iNode++] = n;
8840         if (n == nodes[il1]) {
8841           isFLN = true;
8842         }
8843       }
8844       // add nodes to insert
8845       list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8846       for (; nIt != aNodesToInsert.end(); nIt++) {
8847         poly_nodes[iNode++] = *nIt;
8848       }
8849       // add nodes of face starting from last node of link
8850       while ( nodeIt->more() ) {
8851         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8852         poly_nodes[iNode++] = n;
8853       }
8854     }
8855
8856     // make a new face
8857     newElems.push_back( aMesh->AddPolygonalFace( poly_nodes ));
8858   }
8859
8860   else if ( !theFace->IsQuadratic() )
8861   {
8862     // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8863     int nbLinkNodes = 2 + aNodesToInsert.size();
8864     //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8865     vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8866     linkNodes[ 0 ] = nodes[ il1 ];
8867     linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8868     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8869     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8870       linkNodes[ iNode++ ] = *nIt;
8871     }
8872     // decide how to split a quadrangle: compare possible variants
8873     // and choose which of splits to be a quadrangle
8874     int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad = 0;
8875     if ( nbFaceNodes == 3 ) {
8876       iBestQuad = nbSplits;
8877       i4 = i3;
8878     }
8879     else if ( nbFaceNodes == 4 ) {
8880       SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8881       double aBestRate = DBL_MAX;
8882       for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8883         i1 = 0; i2 = 1;
8884         double aBadRate = 0;
8885         // evaluate elements quality
8886         for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8887           if ( iSplit == iQuad ) {
8888             SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8889                                    linkNodes[ i2++ ],
8890                                    nodes[ i3 ],
8891                                    nodes[ i4 ]);
8892             aBadRate += getBadRate( &quad, aCrit );
8893           }
8894           else {
8895             SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8896                                    linkNodes[ i2++ ],
8897                                    nodes[ iSplit < iQuad ? i4 : i3 ]);
8898             aBadRate += getBadRate( &tria, aCrit );
8899           }
8900         }
8901         // choice
8902         if ( aBadRate < aBestRate ) {
8903           iBestQuad = iQuad;
8904           aBestRate = aBadRate;
8905         }
8906       }
8907     }
8908
8909     // create new elements
8910     i1 = 0; i2 = 1;
8911     for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ )
8912     {
8913       if ( iSplit == iBestQuad )
8914         newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8915                                             linkNodes[ i2++ ],
8916                                             nodes[ i3 ],
8917                                             nodes[ i4 ]));
8918       else
8919         newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8920                                             linkNodes[ i2++ ],
8921                                             nodes[ iSplit < iBestQuad ? i4 : i3 ]));
8922     }
8923
8924     const SMDS_MeshNode* newNodes[ 4 ];
8925     newNodes[ 0 ] = linkNodes[ i1 ];
8926     newNodes[ 1 ] = linkNodes[ i2 ];
8927     newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
8928     newNodes[ 3 ] = nodes[ i4 ];
8929     if (iSplit == iBestQuad)
8930       newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] ));
8931     else
8932       newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] ));
8933
8934   } // end if(!theFace->IsQuadratic())
8935
8936   else { // theFace is quadratic
8937     // we have to split theFace on simple triangles and one simple quadrangle
8938     int tmp = il1/2;
8939     int nbshift = tmp*2;
8940     // shift nodes in nodes[] by nbshift
8941     int i,j;
8942     for(i=0; i<nbshift; i++) {
8943       const SMDS_MeshNode* n = nodes[0];
8944       for(j=0; j<nbFaceNodes-1; j++) {
8945         nodes[j] = nodes[j+1];
8946       }
8947       nodes[nbFaceNodes-1] = n;
8948     }
8949     il1 = il1 - nbshift;
8950     // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
8951     //   n0      n1     n2    n0      n1     n2
8952     //     +-----+-----+        +-----+-----+
8953     //      \         /         |           |
8954     //       \       /          |           |
8955     //      n5+     +n3       n7+           +n3
8956     //         \   /            |           |
8957     //          \ /             |           |
8958     //           +              +-----+-----+
8959     //           n4           n6      n5     n4
8960
8961     // create new elements
8962     int n1,n2,n3;
8963     if ( nbFaceNodes == 6 ) { // quadratic triangle
8964       newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8965       if ( theFace->IsMediumNode(nodes[il1]) ) {
8966         // create quadrangle
8967         newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[5] ));
8968         n1 = 1;
8969         n2 = 2;
8970         n3 = 3;
8971       }
8972       else {
8973         // create quadrangle
8974         newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[5] ));
8975         n1 = 0;
8976         n2 = 1;
8977         n3 = 5;
8978       }
8979     }
8980     else { // nbFaceNodes==8 - quadratic quadrangle
8981       newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8982       newElems.push_back( aMesh->AddFace( nodes[5], nodes[6], nodes[7] ));
8983       newElems.push_back( aMesh->AddFace( nodes[5], nodes[7], nodes[3] ));
8984       if ( theFace->IsMediumNode( nodes[ il1 ])) {
8985         // create quadrangle
8986         newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[7] ));
8987         n1 = 1;
8988         n2 = 2;
8989         n3 = 3;
8990       }
8991       else {
8992         // create quadrangle
8993         newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[7] ));
8994         n1 = 0;
8995         n2 = 1;
8996         n3 = 7;
8997       }
8998     }
8999     // create needed triangles using n1,n2,n3 and inserted nodes
9000     int nbn = 2 + aNodesToInsert.size();
9001     vector<const SMDS_MeshNode*> aNodes(nbn);
9002     aNodes[0    ] = nodes[n1];
9003     aNodes[nbn-1] = nodes[n2];
9004     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9005     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
9006       aNodes[iNode++] = *nIt;
9007     }
9008     for ( i = 1; i < nbn; i++ )
9009       newElems.push_back( aMesh->AddFace( aNodes[i-1], aNodes[i], nodes[n3] ));
9010   }
9011
9012   // remove the old face
9013   for ( size_t i = 0; i < newElems.size(); ++i )
9014     if ( newElems[i] )
9015     {
9016       aMesh->SetMeshElementOnShape( newElems[i], theFace->getshapeId() );
9017       myLastCreatedElems.Append( newElems[i] );
9018     }
9019   ReplaceElemInGroups( theFace, newElems, aMesh );
9020   aMesh->RemoveElement(theFace);
9021
9022 } // InsertNodesIntoLink()
9023
9024 //=======================================================================
9025 //function : UpdateVolumes
9026 //purpose  :
9027 //=======================================================================
9028
9029 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode*        theBetweenNode1,
9030                                       const SMDS_MeshNode*        theBetweenNode2,
9031                                       list<const SMDS_MeshNode*>& theNodesToInsert)
9032 {
9033   myLastCreatedElems.Clear();
9034   myLastCreatedNodes.Clear();
9035
9036   SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
9037   while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
9038     const SMDS_MeshElement* elem = invElemIt->next();
9039
9040     // check, if current volume has link theBetweenNode1 - theBetweenNode2
9041     SMDS_VolumeTool aVolume (elem);
9042     if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
9043       continue;
9044
9045     // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
9046     int iface, nbFaces = aVolume.NbFaces();
9047     vector<const SMDS_MeshNode *> poly_nodes;
9048     vector<int> quantities (nbFaces);
9049
9050     for (iface = 0; iface < nbFaces; iface++) {
9051       int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
9052       // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
9053       const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
9054
9055       for (int inode = 0; inode < nbFaceNodes; inode++) {
9056         poly_nodes.push_back(faceNodes[inode]);
9057
9058         if (nbInserted == 0) {
9059           if (faceNodes[inode] == theBetweenNode1) {
9060             if (faceNodes[inode + 1] == theBetweenNode2) {
9061               nbInserted = theNodesToInsert.size();
9062
9063               // add nodes to insert
9064               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
9065               for (; nIt != theNodesToInsert.end(); nIt++) {
9066                 poly_nodes.push_back(*nIt);
9067               }
9068             }
9069           }
9070           else if (faceNodes[inode] == theBetweenNode2) {
9071             if (faceNodes[inode + 1] == theBetweenNode1) {
9072               nbInserted = theNodesToInsert.size();
9073
9074               // add nodes to insert in reversed order
9075               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
9076               nIt--;
9077               for (; nIt != theNodesToInsert.begin(); nIt--) {
9078                 poly_nodes.push_back(*nIt);
9079               }
9080               poly_nodes.push_back(*nIt);
9081             }
9082           }
9083           else {
9084           }
9085         }
9086       }
9087       quantities[iface] = nbFaceNodes + nbInserted;
9088     }
9089
9090     // Replace the volume
9091     SMESHDS_Mesh *aMesh = GetMeshDS();
9092
9093     if ( SMDS_MeshElement* newElem = aMesh->AddPolyhedralVolume( poly_nodes, quantities ))
9094     {
9095       aMesh->SetMeshElementOnShape( newElem, elem->getshapeId() );
9096       myLastCreatedElems.Append( newElem );
9097       ReplaceElemInGroups( elem, newElem, aMesh );
9098     }
9099     aMesh->RemoveElement( elem );
9100   }
9101 }
9102
9103 namespace
9104 {
9105   //================================================================================
9106   /*!
9107    * \brief Transform any volume into data of SMDSEntity_Polyhedra
9108    */
9109   //================================================================================
9110
9111   void volumeToPolyhedron( const SMDS_MeshElement*         elem,
9112                            vector<const SMDS_MeshNode *> & nodes,
9113                            vector<int> &                   nbNodeInFaces )
9114   {
9115     nodes.clear();
9116     nbNodeInFaces.clear();
9117     SMDS_VolumeTool vTool ( elem );
9118     for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
9119     {
9120       const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
9121       nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
9122       nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
9123     }
9124   }
9125 }
9126
9127 //=======================================================================
9128 /*!
9129  * \brief Convert elements contained in a sub-mesh to quadratic
9130  * \return int - nb of checked elements
9131  */
9132 //=======================================================================
9133
9134 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh *   theSm,
9135                                              SMESH_MesherHelper& theHelper,
9136                                              const bool          theForce3d)
9137 {
9138   int nbElem = 0;
9139   if( !theSm ) return nbElem;
9140
9141   vector<int> nbNodeInFaces;
9142   vector<const SMDS_MeshNode *> nodes;
9143   SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
9144   while(ElemItr->more())
9145   {
9146     nbElem++;
9147     const SMDS_MeshElement* elem = ElemItr->next();
9148     if( !elem ) continue;
9149
9150     // analyse a necessity of conversion
9151     const SMDSAbs_ElementType aType = elem->GetType();
9152     if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
9153       continue;
9154     const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
9155     bool hasCentralNodes = false;
9156     if ( elem->IsQuadratic() )
9157     {
9158       bool alreadyOK;
9159       switch ( aGeomType ) {
9160       case SMDSEntity_Quad_Triangle:
9161       case SMDSEntity_Quad_Quadrangle:
9162       case SMDSEntity_Quad_Hexa:
9163         alreadyOK = !theHelper.GetIsBiQuadratic(); break;
9164
9165       case SMDSEntity_BiQuad_Triangle:
9166       case SMDSEntity_BiQuad_Quadrangle:
9167       case SMDSEntity_TriQuad_Hexa:
9168         alreadyOK = theHelper.GetIsBiQuadratic();
9169         hasCentralNodes = true;
9170         break;
9171       default:
9172         alreadyOK = true;
9173       }
9174       // take into account already present modium nodes
9175       switch ( aType ) {
9176       case SMDSAbs_Volume:
9177         theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
9178       case SMDSAbs_Face:
9179         theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
9180       case SMDSAbs_Edge:
9181         theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
9182       default:;
9183       }
9184       if ( alreadyOK )
9185         continue;
9186     }
9187     // get elem data needed to re-create it
9188     //
9189     const int id      = elem->GetID();
9190     const int nbNodes = elem->NbCornerNodes();
9191     nodes.assign(elem->begin_nodes(), elem->end_nodes());
9192     if ( aGeomType == SMDSEntity_Polyhedra )
9193       nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
9194     else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
9195       volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9196
9197     // remove a linear element
9198     GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9199
9200     // remove central nodes of biquadratic elements (biquad->quad convertion)
9201     if ( hasCentralNodes )
9202       for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
9203         if ( nodes[i]->NbInverseElements() == 0 )
9204           GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
9205
9206     const SMDS_MeshElement* NewElem = 0;
9207
9208     switch( aType )
9209     {
9210     case SMDSAbs_Edge :
9211       {
9212         NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9213         break;
9214       }
9215     case SMDSAbs_Face :
9216       {
9217         switch(nbNodes)
9218         {
9219         case 3:
9220           NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9221           break;
9222         case 4:
9223           NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9224           break;
9225         default:
9226           NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9227         }
9228         break;
9229       }
9230     case SMDSAbs_Volume :
9231       {
9232         switch( aGeomType )
9233         {
9234         case SMDSEntity_Tetra:
9235           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9236           break;
9237         case SMDSEntity_Pyramid:
9238           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9239           break;
9240         case SMDSEntity_Penta:
9241           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9242           break;
9243         case SMDSEntity_Hexa:
9244         case SMDSEntity_Quad_Hexa:
9245         case SMDSEntity_TriQuad_Hexa:
9246           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9247                                         nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9248           break;
9249         case SMDSEntity_Hexagonal_Prism:
9250         default:
9251           NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9252         }
9253         break;
9254       }
9255     default :
9256       continue;
9257     }
9258     ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9259     if( NewElem && NewElem->getshapeId() < 1 )
9260       theSm->AddElement( NewElem );
9261   }
9262   return nbElem;
9263 }
9264 //=======================================================================
9265 //function : ConvertToQuadratic
9266 //purpose  :
9267 //=======================================================================
9268
9269 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
9270 {
9271   SMESHDS_Mesh* meshDS = GetMeshDS();
9272
9273   SMESH_MesherHelper aHelper(*myMesh);
9274
9275   aHelper.SetIsQuadratic( true );
9276   aHelper.SetIsBiQuadratic( theToBiQuad );
9277   aHelper.SetElementsOnShape(true);
9278   aHelper.ToFixNodeParameters( true );
9279
9280   // convert elements assigned to sub-meshes
9281   int nbCheckedElems = 0;
9282   if ( myMesh->HasShapeToMesh() )
9283   {
9284     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9285     {
9286       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9287       while ( smIt->more() ) {
9288         SMESH_subMesh* sm = smIt->next();
9289         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9290           aHelper.SetSubShape( sm->GetSubShape() );
9291           nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9292         }
9293       }
9294     }
9295   }
9296
9297   // convert elements NOT assigned to sub-meshes
9298   int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9299   if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
9300   {
9301     aHelper.SetElementsOnShape(false);
9302     SMESHDS_SubMesh *smDS = 0;
9303
9304     // convert edges
9305     SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9306     while( aEdgeItr->more() )
9307     {
9308       const SMDS_MeshEdge* edge = aEdgeItr->next();
9309       if ( !edge->IsQuadratic() )
9310       {
9311         int                  id = edge->GetID();
9312         const SMDS_MeshNode* n1 = edge->GetNode(0);
9313         const SMDS_MeshNode* n2 = edge->GetNode(1);
9314
9315         meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9316
9317         const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9318         ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9319       }
9320       else
9321       {
9322         aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
9323       }
9324     }
9325
9326     // convert faces
9327     SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9328     while( aFaceItr->more() )
9329     {
9330       const SMDS_MeshFace* face = aFaceItr->next();
9331       if ( !face ) continue;
9332       
9333       const SMDSAbs_EntityType type = face->GetEntityType();
9334       bool alreadyOK;
9335       switch( type )
9336       {
9337       case SMDSEntity_Quad_Triangle:
9338       case SMDSEntity_Quad_Quadrangle:
9339         alreadyOK = !theToBiQuad;
9340         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9341         break;
9342       case SMDSEntity_BiQuad_Triangle:
9343       case SMDSEntity_BiQuad_Quadrangle:
9344         alreadyOK = theToBiQuad;
9345         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9346         break;
9347       default: alreadyOK = false;
9348       }
9349       if ( alreadyOK )
9350         continue;
9351
9352       const int id = face->GetID();
9353       vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9354
9355       meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9356
9357       SMDS_MeshFace * NewFace = 0;
9358       switch( type )
9359       {
9360       case SMDSEntity_Triangle:
9361       case SMDSEntity_Quad_Triangle:
9362       case SMDSEntity_BiQuad_Triangle:
9363         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9364         if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
9365           GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
9366         break;
9367
9368       case SMDSEntity_Quadrangle:
9369       case SMDSEntity_Quad_Quadrangle:
9370       case SMDSEntity_BiQuad_Quadrangle:
9371         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9372         if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
9373           GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
9374         break;
9375
9376       default:;
9377         NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9378       }
9379       ReplaceElemInGroups( face, NewFace, GetMeshDS());
9380     }
9381
9382     // convert volumes
9383     vector<int> nbNodeInFaces;
9384     SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9385     while(aVolumeItr->more())
9386     {
9387       const SMDS_MeshVolume* volume = aVolumeItr->next();
9388       if ( !volume ) continue;
9389
9390       const SMDSAbs_EntityType type = volume->GetEntityType();
9391       if ( volume->IsQuadratic() )
9392       {
9393         bool alreadyOK;
9394         switch ( type )
9395         {
9396         case SMDSEntity_Quad_Hexa:    alreadyOK = !theToBiQuad; break;
9397         case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9398         default:                      alreadyOK = true;
9399         }
9400         if ( alreadyOK )
9401         {
9402           aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
9403           continue;
9404         }
9405       }
9406       const int id = volume->GetID();
9407       vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9408       if ( type == SMDSEntity_Polyhedra )
9409         nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
9410       else if ( type == SMDSEntity_Hexagonal_Prism )
9411         volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9412
9413       meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9414
9415       SMDS_MeshVolume * NewVolume = 0;
9416       switch ( type )
9417       {
9418       case SMDSEntity_Tetra:
9419         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9420         break;
9421       case SMDSEntity_Hexa:
9422       case SMDSEntity_Quad_Hexa:
9423       case SMDSEntity_TriQuad_Hexa:
9424         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9425                                       nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9426         for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
9427           if ( nodes[i]->NbInverseElements() == 0 )
9428             GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9429         break;
9430       case SMDSEntity_Pyramid:
9431         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9432                                       nodes[3], nodes[4], id, theForce3d);
9433         break;
9434       case SMDSEntity_Penta:
9435         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9436                                       nodes[3], nodes[4], nodes[5], id, theForce3d);
9437         break;
9438       case SMDSEntity_Hexagonal_Prism:
9439       default:
9440         NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9441       }
9442       ReplaceElemInGroups(volume, NewVolume, meshDS);
9443     }
9444   }
9445
9446   if ( !theForce3d )
9447   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9448     // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9449     // aHelper.FixQuadraticElements(myError);
9450     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9451   }
9452 }
9453
9454 //================================================================================
9455 /*!
9456  * \brief Makes given elements quadratic
9457  *  \param theForce3d - if true, the medium nodes will be placed in the middle of link
9458  *  \param theElements - elements to make quadratic
9459  */
9460 //================================================================================
9461
9462 void SMESH_MeshEditor::ConvertToQuadratic(const bool        theForce3d,
9463                                           TIDSortedElemSet& theElements,
9464                                           const bool        theToBiQuad)
9465 {
9466   if ( theElements.empty() ) return;
9467
9468   // we believe that all theElements are of the same type
9469   const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9470
9471   // get all nodes shared by theElements
9472   TIDSortedNodeSet allNodes;
9473   TIDSortedElemSet::iterator eIt = theElements.begin();
9474   for ( ; eIt != theElements.end(); ++eIt )
9475     allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9476
9477   // complete theElements with elements of lower dim whose all nodes are in allNodes
9478
9479   TIDSortedElemSet quadAdjacentElems    [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9480   TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9481   TIDSortedNodeSet::iterator nIt = allNodes.begin();
9482   for ( ; nIt != allNodes.end(); ++nIt )
9483   {
9484     const SMDS_MeshNode* n = *nIt;
9485     SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9486     while ( invIt->more() )
9487     {
9488       const SMDS_MeshElement*      e = invIt->next();
9489       const SMDSAbs_ElementType type = e->GetType();
9490       if ( e->IsQuadratic() )
9491       {
9492         quadAdjacentElems[ type ].insert( e );
9493
9494         bool alreadyOK;
9495         switch ( e->GetEntityType() ) {
9496         case SMDSEntity_Quad_Triangle:
9497         case SMDSEntity_Quad_Quadrangle:
9498         case SMDSEntity_Quad_Hexa:         alreadyOK = !theToBiQuad; break;
9499         case SMDSEntity_BiQuad_Triangle:
9500         case SMDSEntity_BiQuad_Quadrangle:
9501         case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; break;
9502         default:                           alreadyOK = true;
9503         }
9504         if ( alreadyOK )
9505           continue;
9506       }
9507       if ( type >= elemType )
9508         continue; // same type or more complex linear element
9509
9510       if ( !checkedAdjacentElems[ type ].insert( e ).second )
9511         continue; // e is already checked
9512
9513       // check nodes
9514       bool allIn = true;
9515       SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9516       while ( nodeIt->more() && allIn )
9517         allIn = allNodes.count( nodeIt->next() );
9518       if ( allIn )
9519         theElements.insert(e );
9520     }
9521   }
9522
9523   SMESH_MesherHelper helper(*myMesh);
9524   helper.SetIsQuadratic( true );
9525   helper.SetIsBiQuadratic( theToBiQuad );
9526
9527   // add links of quadratic adjacent elements to the helper
9528
9529   if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9530     for ( eIt  = quadAdjacentElems[SMDSAbs_Edge].begin();
9531           eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9532     {
9533       helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9534     }
9535   if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9536     for ( eIt  = quadAdjacentElems[SMDSAbs_Face].begin();
9537           eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9538     {
9539       helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9540     }
9541   if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9542     for ( eIt  = quadAdjacentElems[SMDSAbs_Volume].begin();
9543           eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9544     {
9545       helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9546     }
9547
9548   // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9549
9550   SMESHDS_Mesh*  meshDS = GetMeshDS();
9551   SMESHDS_SubMesh* smDS = 0;
9552   for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9553   {
9554     const SMDS_MeshElement* elem = *eIt;
9555
9556     bool alreadyOK;
9557     int nbCentralNodes = 0;
9558     switch ( elem->GetEntityType() ) {
9559       // linear convertible
9560     case SMDSEntity_Edge:
9561     case SMDSEntity_Triangle:
9562     case SMDSEntity_Quadrangle:
9563     case SMDSEntity_Tetra:
9564     case SMDSEntity_Pyramid:
9565     case SMDSEntity_Hexa:
9566     case SMDSEntity_Penta:             alreadyOK = false;       nbCentralNodes = 0; break;
9567       // quadratic that can become bi-quadratic
9568     case SMDSEntity_Quad_Triangle:
9569     case SMDSEntity_Quad_Quadrangle:
9570     case SMDSEntity_Quad_Hexa:         alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9571       // bi-quadratic
9572     case SMDSEntity_BiQuad_Triangle:
9573     case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9574     case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9575       // the rest
9576     default:                           alreadyOK = true;
9577     }
9578     if ( alreadyOK ) continue;
9579
9580     const SMDSAbs_ElementType type = elem->GetType();
9581     const int                   id = elem->GetID();
9582     const int              nbNodes = elem->NbCornerNodes();
9583     vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9584
9585     helper.SetSubShape( elem->getshapeId() );
9586
9587     if ( !smDS || !smDS->Contains( elem ))
9588       smDS = meshDS->MeshElements( elem->getshapeId() );
9589     meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9590
9591     SMDS_MeshElement * newElem = 0;
9592     switch( nbNodes )
9593     {
9594     case 4: // cases for most frequently used element types go first (for optimization)
9595       if ( type == SMDSAbs_Volume )
9596         newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9597       else
9598         newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9599       break;
9600     case 8:
9601       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9602                                  nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9603       break;
9604     case 3:
9605       newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], id, theForce3d);
9606       break;
9607     case 2:
9608       newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9609       break;
9610     case 5:
9611       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9612                                  nodes[4], id, theForce3d);
9613       break;
9614     case 6:
9615       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9616                                  nodes[4], nodes[5], id, theForce3d);
9617       break;
9618     default:;
9619     }
9620     ReplaceElemInGroups( elem, newElem, meshDS);
9621     if( newElem && smDS )
9622       smDS->AddElement( newElem );
9623
9624      // remove central nodes
9625     for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9626       if ( nodes[i]->NbInverseElements() == 0 )
9627         meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9628
9629   } // loop on theElements
9630
9631   if ( !theForce3d )
9632   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9633     // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9634     // helper.FixQuadraticElements( myError );
9635     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9636   }
9637 }
9638
9639 //=======================================================================
9640 /*!
9641  * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9642  * \return int - nb of checked elements
9643  */
9644 //=======================================================================
9645
9646 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh *    theSm,
9647                                      SMDS_ElemIteratorPtr theItr,
9648                                      const int            theShapeID)
9649 {
9650   int nbElem = 0;
9651   SMESHDS_Mesh* meshDS = GetMeshDS();
9652   ElemFeatures elemType;
9653   vector<const SMDS_MeshNode *> nodes;
9654
9655   while( theItr->more() )
9656   {
9657     const SMDS_MeshElement* elem = theItr->next();
9658     nbElem++;
9659     if( elem && elem->IsQuadratic())
9660     {
9661       // get elem data
9662       int nbCornerNodes = elem->NbCornerNodes();
9663       nodes.assign( elem->begin_nodes(), elem->end_nodes() );
9664
9665       elemType.Init( elem, /*basicOnly=*/false ).SetID( elem->GetID() ).SetQuad( false );
9666
9667       //remove a quadratic element
9668       if ( !theSm || !theSm->Contains( elem ))
9669         theSm = meshDS->MeshElements( elem->getshapeId() );
9670       meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9671
9672       // remove medium nodes
9673       for ( size_t i = nbCornerNodes; i < nodes.size(); ++i )
9674         if ( nodes[i]->NbInverseElements() == 0 )
9675           meshDS->RemoveFreeNode( nodes[i], theSm );
9676
9677       // add a linear element
9678       nodes.resize( nbCornerNodes );
9679       SMDS_MeshElement * newElem = AddElement( nodes, elemType );
9680       ReplaceElemInGroups(elem, newElem, meshDS);
9681       if( theSm && newElem )
9682         theSm->AddElement( newElem );
9683     }
9684   }
9685   return nbElem;
9686 }
9687
9688 //=======================================================================
9689 //function : ConvertFromQuadratic
9690 //purpose  :
9691 //=======================================================================
9692
9693 bool SMESH_MeshEditor::ConvertFromQuadratic()
9694 {
9695   int nbCheckedElems = 0;
9696   if ( myMesh->HasShapeToMesh() )
9697   {
9698     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9699     {
9700       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9701       while ( smIt->more() ) {
9702         SMESH_subMesh* sm = smIt->next();
9703         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9704           nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9705       }
9706     }
9707   }
9708
9709   int totalNbElems =
9710     GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9711   if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9712   {
9713     SMESHDS_SubMesh *aSM = 0;
9714     removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9715   }
9716
9717   return true;
9718 }
9719
9720 namespace
9721 {
9722   //================================================================================
9723   /*!
9724    * \brief Return true if all medium nodes of the element are in the node set
9725    */
9726   //================================================================================
9727
9728   bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9729   {
9730     for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9731       if ( !nodeSet.count( elem->GetNode(i) ))
9732         return false;
9733     return true;
9734   }
9735 }
9736
9737 //================================================================================
9738 /*!
9739  * \brief Makes given elements linear
9740  */
9741 //================================================================================
9742
9743 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9744 {
9745   if ( theElements.empty() ) return;
9746
9747   // collect IDs of medium nodes of theElements; some of these nodes will be removed
9748   set<int> mediumNodeIDs;
9749   TIDSortedElemSet::iterator eIt = theElements.begin();
9750   for ( ; eIt != theElements.end(); ++eIt )
9751   {
9752     const SMDS_MeshElement* e = *eIt;
9753     for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9754       mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9755   }
9756
9757   // replace given elements by linear ones
9758   SMDS_ElemIteratorPtr elemIt = elemSetIterator( theElements );
9759   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9760
9761   // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9762   // except those elements sharing medium nodes of quadratic element whose medium nodes
9763   // are not all in mediumNodeIDs
9764
9765   // get remaining medium nodes
9766   TIDSortedNodeSet mediumNodes;
9767   set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9768   for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9769     if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9770       mediumNodes.insert( mediumNodes.end(), n );
9771
9772   // find more quadratic elements to convert
9773   TIDSortedElemSet moreElemsToConvert;
9774   TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9775   for ( ; nIt != mediumNodes.end(); ++nIt )
9776   {
9777     SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9778     while ( invIt->more() )
9779     {
9780       const SMDS_MeshElement* e = invIt->next();
9781       if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9782       {
9783         // find a more complex element including e and
9784         // whose medium nodes are not in mediumNodes
9785         bool complexFound = false;
9786         for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9787         {
9788           SMDS_ElemIteratorPtr invIt2 =
9789             (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9790           while ( invIt2->more() )
9791           {
9792             const SMDS_MeshElement* eComplex = invIt2->next();
9793             if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9794             {
9795               int nbCommonNodes = SMESH_MeshAlgos::GetCommonNodes( e, eComplex ).size();
9796               if ( nbCommonNodes == e->NbNodes())
9797               {
9798                 complexFound = true;
9799                 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9800                 break;
9801               }
9802             }
9803           }
9804         }
9805         if ( !complexFound )
9806           moreElemsToConvert.insert( e );
9807       }
9808     }
9809   }
9810   elemIt = elemSetIterator( moreElemsToConvert );
9811   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9812 }
9813
9814 //=======================================================================
9815 //function : SewSideElements
9816 //purpose  :
9817 //=======================================================================
9818
9819 SMESH_MeshEditor::Sew_Error
9820 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet&    theSide1,
9821                                    TIDSortedElemSet&    theSide2,
9822                                    const SMDS_MeshNode* theFirstNode1,
9823                                    const SMDS_MeshNode* theFirstNode2,
9824                                    const SMDS_MeshNode* theSecondNode1,
9825                                    const SMDS_MeshNode* theSecondNode2)
9826 {
9827   myLastCreatedElems.Clear();
9828   myLastCreatedNodes.Clear();
9829
9830   if ( theSide1.size() != theSide2.size() )
9831     return SEW_DIFF_NB_OF_ELEMENTS;
9832
9833   Sew_Error aResult = SEW_OK;
9834   // Algo:
9835   // 1. Build set of faces representing each side
9836   // 2. Find which nodes of the side 1 to merge with ones on the side 2
9837   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9838
9839   // =======================================================================
9840   // 1. Build set of faces representing each side:
9841   // =======================================================================
9842   // a. build set of nodes belonging to faces
9843   // b. complete set of faces: find missing faces whose nodes are in set of nodes
9844   // c. create temporary faces representing side of volumes if correspondent
9845   //    face does not exist
9846
9847   SMESHDS_Mesh* aMesh = GetMeshDS();
9848   // TODO algoritm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9849   //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9850   TIDSortedElemSet             faceSet1, faceSet2;
9851   set<const SMDS_MeshElement*> volSet1,  volSet2;
9852   set<const SMDS_MeshNode*>    nodeSet1, nodeSet2;
9853   TIDSortedElemSet             * faceSetPtr[] = { &faceSet1, &faceSet2 };
9854   set<const SMDS_MeshElement*> *  volSetPtr[] = { &volSet1,  &volSet2  };
9855   set<const SMDS_MeshNode*>    * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9856   TIDSortedElemSet             * elemSetPtr[] = { &theSide1, &theSide2 };
9857   int iSide, iFace, iNode;
9858
9859   list<const SMDS_MeshElement* > tempFaceList;
9860   for ( iSide = 0; iSide < 2; iSide++ ) {
9861     set<const SMDS_MeshNode*>    * nodeSet = nodeSetPtr[ iSide ];
9862     TIDSortedElemSet             * elemSet = elemSetPtr[ iSide ];
9863     TIDSortedElemSet             * faceSet = faceSetPtr[ iSide ];
9864     set<const SMDS_MeshElement*> * volSet  = volSetPtr [ iSide ];
9865     set<const SMDS_MeshElement*>::iterator vIt;
9866     TIDSortedElemSet::iterator eIt;
9867     set<const SMDS_MeshNode*>::iterator    nIt;
9868
9869     // check that given nodes belong to given elements
9870     const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9871     const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9872     int firstIndex = -1, secondIndex = -1;
9873     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9874       const SMDS_MeshElement* elem = *eIt;
9875       if ( firstIndex  < 0 ) firstIndex  = elem->GetNodeIndex( n1 );
9876       if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9877       if ( firstIndex > -1 && secondIndex > -1 ) break;
9878     }
9879     if ( firstIndex < 0 || secondIndex < 0 ) {
9880       // we can simply return until temporary faces created
9881       return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9882     }
9883
9884     // -----------------------------------------------------------
9885     // 1a. Collect nodes of existing faces
9886     //     and build set of face nodes in order to detect missing
9887     //     faces corresponding to sides of volumes
9888     // -----------------------------------------------------------
9889
9890     set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9891
9892     // loop on the given element of a side
9893     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9894       //const SMDS_MeshElement* elem = *eIt;
9895       const SMDS_MeshElement* elem = *eIt;
9896       if ( elem->GetType() == SMDSAbs_Face ) {
9897         faceSet->insert( elem );
9898         set <const SMDS_MeshNode*> faceNodeSet;
9899         SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9900         while ( nodeIt->more() ) {
9901           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9902           nodeSet->insert( n );
9903           faceNodeSet.insert( n );
9904         }
9905         setOfFaceNodeSet.insert( faceNodeSet );
9906       }
9907       else if ( elem->GetType() == SMDSAbs_Volume )
9908         volSet->insert( elem );
9909     }
9910     // ------------------------------------------------------------------------------
9911     // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9912     // ------------------------------------------------------------------------------
9913
9914     for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9915       SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9916       while ( fIt->more() ) { // loop on faces sharing a node
9917         const SMDS_MeshElement* f = fIt->next();
9918         if ( faceSet->find( f ) == faceSet->end() ) {
9919           // check if all nodes are in nodeSet and
9920           // complete setOfFaceNodeSet if they are
9921           set <const SMDS_MeshNode*> faceNodeSet;
9922           SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9923           bool allInSet = true;
9924           while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9925             const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9926             if ( nodeSet->find( n ) == nodeSet->end() )
9927               allInSet = false;
9928             else
9929               faceNodeSet.insert( n );
9930           }
9931           if ( allInSet ) {
9932             faceSet->insert( f );
9933             setOfFaceNodeSet.insert( faceNodeSet );
9934           }
9935         }
9936       }
9937     }
9938
9939     // -------------------------------------------------------------------------
9940     // 1c. Create temporary faces representing sides of volumes if correspondent
9941     //     face does not exist
9942     // -------------------------------------------------------------------------
9943
9944     if ( !volSet->empty() ) {
9945       //int nodeSetSize = nodeSet->size();
9946
9947       // loop on given volumes
9948       for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
9949         SMDS_VolumeTool vol (*vIt);
9950         // loop on volume faces: find free faces
9951         // --------------------------------------
9952         list<const SMDS_MeshElement* > freeFaceList;
9953         for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
9954           if ( !vol.IsFreeFace( iFace ))
9955             continue;
9956           // check if there is already a face with same nodes in a face set
9957           const SMDS_MeshElement* aFreeFace = 0;
9958           const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
9959           int nbNodes = vol.NbFaceNodes( iFace );
9960           set <const SMDS_MeshNode*> faceNodeSet;
9961           vol.GetFaceNodes( iFace, faceNodeSet );
9962           bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
9963           if ( isNewFace ) {
9964             // no such a face is given but it still can exist, check it
9965             vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
9966             aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
9967           }
9968           if ( !aFreeFace ) {
9969             // create a temporary face
9970             if ( nbNodes == 3 ) {
9971               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
9972               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
9973             }
9974             else if ( nbNodes == 4 ) {
9975               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9976               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9977             }
9978             else {
9979               vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9980               //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
9981               aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
9982             }
9983             if ( aFreeFace )
9984               tempFaceList.push_back( aFreeFace );
9985           }
9986
9987           if ( aFreeFace )
9988             freeFaceList.push_back( aFreeFace );
9989
9990         } // loop on faces of a volume
9991
9992         // choose one of several free faces of a volume
9993         // --------------------------------------------
9994         if ( freeFaceList.size() > 1 ) {
9995           // choose a face having max nb of nodes shared by other elems of a side
9996           int maxNbNodes = -1;
9997           list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
9998           while ( fIt != freeFaceList.end() ) { // loop on free faces
9999             int nbSharedNodes = 0;
10000             SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10001             while ( nodeIt->more() ) { // loop on free face nodes
10002               const SMDS_MeshNode* n =
10003                 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10004               SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
10005               while ( invElemIt->more() ) {
10006                 const SMDS_MeshElement* e = invElemIt->next();
10007                 nbSharedNodes += faceSet->count( e );
10008                 nbSharedNodes += elemSet->count( e );
10009               }
10010             }
10011             if ( nbSharedNodes > maxNbNodes ) {
10012               maxNbNodes = nbSharedNodes;
10013               freeFaceList.erase( freeFaceList.begin(), fIt++ );
10014             }
10015             else if ( nbSharedNodes == maxNbNodes ) {
10016               fIt++;
10017             }
10018             else {
10019               freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
10020             }
10021           }
10022           if ( freeFaceList.size() > 1 )
10023           {
10024             // could not choose one face, use another way
10025             // choose a face most close to the bary center of the opposite side
10026             gp_XYZ aBC( 0., 0., 0. );
10027             set <const SMDS_MeshNode*> addedNodes;
10028             TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
10029             eIt = elemSet2->begin();
10030             for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
10031               SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
10032               while ( nodeIt->more() ) { // loop on free face nodes
10033                 const SMDS_MeshNode* n =
10034                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10035                 if ( addedNodes.insert( n ).second )
10036                   aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
10037               }
10038             }
10039             aBC /= addedNodes.size();
10040             double minDist = DBL_MAX;
10041             fIt = freeFaceList.begin();
10042             while ( fIt != freeFaceList.end() ) { // loop on free faces
10043               double dist = 0;
10044               SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10045               while ( nodeIt->more() ) { // loop on free face nodes
10046                 const SMDS_MeshNode* n =
10047                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10048                 gp_XYZ p( n->X(),n->Y(),n->Z() );
10049                 dist += ( aBC - p ).SquareModulus();
10050               }
10051               if ( dist < minDist ) {
10052                 minDist = dist;
10053                 freeFaceList.erase( freeFaceList.begin(), fIt++ );
10054               }
10055               else
10056                 fIt = freeFaceList.erase( fIt++ );
10057             }
10058           }
10059         } // choose one of several free faces of a volume
10060
10061         if ( freeFaceList.size() == 1 ) {
10062           const SMDS_MeshElement* aFreeFace = freeFaceList.front();
10063           faceSet->insert( aFreeFace );
10064           // complete a node set with nodes of a found free face
10065           //           for ( iNode = 0; iNode < ; iNode++ )
10066           //             nodeSet->insert( fNodes[ iNode ] );
10067         }
10068
10069       } // loop on volumes of a side
10070
10071       //       // complete a set of faces if new nodes in a nodeSet appeared
10072       //       // ----------------------------------------------------------
10073       //       if ( nodeSetSize != nodeSet->size() ) {
10074       //         for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10075       //           SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10076       //           while ( fIt->more() ) { // loop on faces sharing a node
10077       //             const SMDS_MeshElement* f = fIt->next();
10078       //             if ( faceSet->find( f ) == faceSet->end() ) {
10079       //               // check if all nodes are in nodeSet and
10080       //               // complete setOfFaceNodeSet if they are
10081       //               set <const SMDS_MeshNode*> faceNodeSet;
10082       //               SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10083       //               bool allInSet = true;
10084       //               while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10085       //                 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10086       //                 if ( nodeSet->find( n ) == nodeSet->end() )
10087       //                   allInSet = false;
10088       //                 else
10089       //                   faceNodeSet.insert( n );
10090       //               }
10091       //               if ( allInSet ) {
10092       //                 faceSet->insert( f );
10093       //                 setOfFaceNodeSet.insert( faceNodeSet );
10094       //               }
10095       //             }
10096       //           }
10097       //         }
10098       //       }
10099     } // Create temporary faces, if there are volumes given
10100   } // loop on sides
10101
10102   if ( faceSet1.size() != faceSet2.size() ) {
10103     // delete temporary faces: they are in reverseElements of actual nodes
10104 //    SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10105 //    while ( tmpFaceIt->more() )
10106 //      aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10107 //    list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10108 //    for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10109 //      aMesh->RemoveElement(*tmpFaceIt);
10110     MESSAGE("Diff nb of faces");
10111     return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10112   }
10113
10114   // ============================================================
10115   // 2. Find nodes to merge:
10116   //              bind a node to remove to a node to put instead
10117   // ============================================================
10118
10119   TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
10120   if ( theFirstNode1 != theFirstNode2 )
10121     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10122   if ( theSecondNode1 != theSecondNode2 )
10123     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10124
10125   LinkID_Gen aLinkID_Gen( GetMeshDS() );
10126   set< long > linkIdSet; // links to process
10127   linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
10128
10129   typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
10130   list< NLink > linkList[2];
10131   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10132   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10133   // loop on links in linkList; find faces by links and append links
10134   // of the found faces to linkList
10135   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10136   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
10137   {
10138     NLink link[] = { *linkIt[0], *linkIt[1] };
10139     long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
10140     if ( !linkIdSet.count( linkID ) )
10141       continue;
10142
10143     // by links, find faces in the face sets,
10144     // and find indices of link nodes in the found faces;
10145     // in a face set, there is only one or no face sharing a link
10146     // ---------------------------------------------------------------
10147
10148     const SMDS_MeshElement* face[] = { 0, 0 };
10149     vector<const SMDS_MeshNode*> fnodes[2];
10150     int iLinkNode[2][2];
10151     TIDSortedElemSet avoidSet;
10152     for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10153       const SMDS_MeshNode* n1 = link[iSide].first;
10154       const SMDS_MeshNode* n2 = link[iSide].second;
10155       //cout << "Side " << iSide << " ";
10156       //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
10157       // find a face by two link nodes
10158       face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
10159                                                       *faceSetPtr[ iSide ], avoidSet,
10160                                                       &iLinkNode[iSide][0],
10161                                                       &iLinkNode[iSide][1] );
10162       if ( face[ iSide ])
10163       {
10164         //cout << " F " << face[ iSide]->GetID() <<endl;
10165         faceSetPtr[ iSide ]->erase( face[ iSide ]);
10166         // put face nodes to fnodes
10167         if ( face[ iSide ]->IsQuadratic() )
10168         {
10169           // use interlaced nodes iterator
10170           const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>( face[ iSide ]);
10171           if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
10172           SMDS_ElemIteratorPtr nIter = F->interlacedNodesElemIterator();
10173           while ( nIter->more() )
10174             fnodes[ iSide ].push_back( cast2Node( nIter->next() ));
10175         }
10176         else
10177         {
10178           fnodes[ iSide ].assign( face[ iSide ]->begin_nodes(),
10179                                   face[ iSide ]->end_nodes() );
10180         }
10181         fnodes[ iSide ].push_back( fnodes[ iSide ].front());
10182       }
10183     }
10184
10185     // check similarity of elements of the sides
10186     if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10187       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10188       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10189         aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10190       }
10191       else {
10192         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10193       }
10194       break; // do not return because it's necessary to remove tmp faces
10195     }
10196
10197     // set nodes to merge
10198     // -------------------
10199
10200     if ( face[0] && face[1] )  {
10201       const int nbNodes = face[0]->NbNodes();
10202       if ( nbNodes != face[1]->NbNodes() ) {
10203         MESSAGE("Diff nb of face nodes");
10204         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10205         break; // do not return because it s necessary to remove tmp faces
10206       }
10207       bool reverse[] = { false, false }; // order of nodes in the link
10208       for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10209         // analyse link orientation in faces
10210         int i1 = iLinkNode[ iSide ][ 0 ];
10211         int i2 = iLinkNode[ iSide ][ 1 ];
10212         reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10213       }
10214       int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
10215       int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
10216       for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
10217       {
10218         nReplaceMap.insert  ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
10219                                           fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
10220       }
10221
10222       // add other links of the faces to linkList
10223       // -----------------------------------------
10224
10225       for ( iNode = 0; iNode < nbNodes; iNode++ )  {
10226         linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
10227         pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10228         if ( !iter_isnew.second ) { // already in a set: no need to process
10229           linkIdSet.erase( iter_isnew.first );
10230         }
10231         else // new in set == encountered for the first time: add
10232         {
10233           const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
10234           const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
10235           linkList[0].push_back ( NLink( n1, n2 ));
10236           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10237         }
10238       }
10239     } // 2 faces found
10240
10241     if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
10242       break;
10243
10244   } // loop on link lists
10245
10246   if ( aResult == SEW_OK &&
10247        ( //linkIt[0] != linkList[0].end() ||
10248          !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10249     MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10250              " " << (faceSetPtr[1]->empty()));
10251     aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10252   }
10253
10254   // ====================================================================
10255   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10256   // ====================================================================
10257
10258   // delete temporary faces
10259 //  SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10260 //  while ( tmpFaceIt->more() )
10261 //    aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10262   list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10263   for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10264     aMesh->RemoveElement(*tmpFaceIt);
10265
10266   if ( aResult != SEW_OK)
10267     return aResult;
10268
10269   list< int > nodeIDsToRemove;
10270   vector< const SMDS_MeshNode*> nodes;
10271   ElemFeatures elemType;
10272
10273   // loop on nodes replacement map
10274   TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10275   for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10276     if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second )
10277     {
10278       const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10279       nodeIDsToRemove.push_back( nToRemove->GetID() );
10280       // loop on elements sharing nToRemove
10281       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10282       while ( invElemIt->more() ) {
10283         const SMDS_MeshElement* e = invElemIt->next();
10284         // get a new suite of nodes: make replacement
10285         int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10286         nodes.resize( nbNodes );
10287         SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10288         while ( nIt->more() ) {
10289           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
10290           nnIt = nReplaceMap.find( n );
10291           if ( nnIt != nReplaceMap.end() ) {
10292             nbReplaced++;
10293             n = (*nnIt).second;
10294           }
10295           nodes[ i++ ] = n;
10296         }
10297         //       if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10298         //         elemIDsToRemove.push_back( e->GetID() );
10299         //       else
10300         if ( nbReplaced )
10301         {
10302           elemType.Init( e, /*basicOnly=*/false ).SetID( e->GetID() );
10303           aMesh->RemoveElement( e );
10304
10305           if ( SMDS_MeshElement* newElem = this->AddElement( nodes, elemType ))
10306           {
10307             AddToSameGroups( newElem, e, aMesh );
10308             if ( int aShapeId = e->getshapeId() )
10309               aMesh->SetMeshElementOnShape( newElem, aShapeId );
10310           }
10311         }
10312       }
10313     }
10314
10315   Remove( nodeIDsToRemove, true );
10316
10317   return aResult;
10318 }
10319
10320 //================================================================================
10321 /*!
10322  * \brief Find corresponding nodes in two sets of faces
10323  * \param theSide1 - first face set
10324  * \param theSide2 - second first face
10325  * \param theFirstNode1 - a boundary node of set 1
10326  * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10327  * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10328  * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10329  * \param nReplaceMap - output map of corresponding nodes
10330  * \return bool  - is a success or not
10331  */
10332 //================================================================================
10333
10334 #ifdef _DEBUG_
10335 //#define DEBUG_MATCHING_NODES
10336 #endif
10337
10338 SMESH_MeshEditor::Sew_Error
10339 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10340                                     set<const SMDS_MeshElement*>& theSide2,
10341                                     const SMDS_MeshNode*          theFirstNode1,
10342                                     const SMDS_MeshNode*          theFirstNode2,
10343                                     const SMDS_MeshNode*          theSecondNode1,
10344                                     const SMDS_MeshNode*          theSecondNode2,
10345                                     TNodeNodeMap &                nReplaceMap)
10346 {
10347   set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10348
10349   nReplaceMap.clear();
10350   if ( theFirstNode1 != theFirstNode2 )
10351     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10352   if ( theSecondNode1 != theSecondNode2 )
10353     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10354
10355   set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10356   linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10357
10358   list< NLink > linkList[2];
10359   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10360   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10361
10362   // loop on links in linkList; find faces by links and append links
10363   // of the found faces to linkList
10364   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10365   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10366     NLink link[] = { *linkIt[0], *linkIt[1] };
10367     if ( linkSet.find( link[0] ) == linkSet.end() )
10368       continue;
10369
10370     // by links, find faces in the face sets,
10371     // and find indices of link nodes in the found faces;
10372     // in a face set, there is only one or no face sharing a link
10373     // ---------------------------------------------------------------
10374
10375     const SMDS_MeshElement* face[] = { 0, 0 };
10376     list<const SMDS_MeshNode*> notLinkNodes[2];
10377     //bool reverse[] = { false, false }; // order of notLinkNodes
10378     int nbNodes[2];
10379     for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10380     {
10381       const SMDS_MeshNode* n1 = link[iSide].first;
10382       const SMDS_MeshNode* n2 = link[iSide].second;
10383       set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10384       set< const SMDS_MeshElement* > facesOfNode1;
10385       for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10386       {
10387         // during a loop of the first node, we find all faces around n1,
10388         // during a loop of the second node, we find one face sharing both n1 and n2
10389         const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10390         SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10391         while ( fIt->more() ) { // loop on faces sharing a node
10392           const SMDS_MeshElement* f = fIt->next();
10393           if (faceSet->find( f ) != faceSet->end() && // f is in face set
10394               ! facesOfNode1.insert( f ).second ) // f encounters twice
10395           {
10396             if ( face[ iSide ] ) {
10397               MESSAGE( "2 faces per link " );
10398               return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10399             }
10400             face[ iSide ] = f;
10401             faceSet->erase( f );
10402
10403             // get not link nodes
10404             int nbN = f->NbNodes();
10405             if ( f->IsQuadratic() )
10406               nbN /= 2;
10407             nbNodes[ iSide ] = nbN;
10408             list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10409             int i1 = f->GetNodeIndex( n1 );
10410             int i2 = f->GetNodeIndex( n2 );
10411             int iEnd = nbN, iBeg = -1, iDelta = 1;
10412             bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10413             if ( reverse ) {
10414               std::swap( iEnd, iBeg ); iDelta = -1;
10415             }
10416             int i = i2;
10417             while ( true ) {
10418               i += iDelta;
10419               if ( i == iEnd ) i = iBeg + iDelta;
10420               if ( i == i1 ) break;
10421               nodes.push_back ( f->GetNode( i ) );
10422             }
10423           }
10424         }
10425       }
10426     }
10427     // check similarity of elements of the sides
10428     if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10429       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10430       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10431         return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10432       }
10433       else {
10434         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10435       }
10436     }
10437
10438     // set nodes to merge
10439     // -------------------
10440
10441     if ( face[0] && face[1] )  {
10442       if ( nbNodes[0] != nbNodes[1] ) {
10443         MESSAGE("Diff nb of face nodes");
10444         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10445       }
10446 #ifdef DEBUG_MATCHING_NODES
10447       MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10448                 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10449                 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10450 #endif
10451       int nbN = nbNodes[0];
10452       {
10453         list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10454         list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10455         for ( int i = 0 ; i < nbN - 2; ++i ) {
10456 #ifdef DEBUG_MATCHING_NODES
10457           MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10458 #endif
10459           nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10460         }
10461       }
10462
10463       // add other links of the face 1 to linkList
10464       // -----------------------------------------
10465
10466       const SMDS_MeshElement* f0 = face[0];
10467       const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10468       for ( int i = 0; i < nbN; i++ )
10469       {
10470         const SMDS_MeshNode* n2 = f0->GetNode( i );
10471         pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10472           linkSet.insert( SMESH_TLink( n1, n2 ));
10473         if ( !iter_isnew.second ) { // already in a set: no need to process
10474           linkSet.erase( iter_isnew.first );
10475         }
10476         else // new in set == encountered for the first time: add
10477         {
10478 #ifdef DEBUG_MATCHING_NODES
10479           MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10480                     << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10481 #endif
10482           linkList[0].push_back ( NLink( n1, n2 ));
10483           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10484         }
10485         n1 = n2;
10486       }
10487     } // 2 faces found
10488   } // loop on link lists
10489
10490   return SEW_OK;
10491 }
10492
10493 //================================================================================
10494 /*!
10495  * \brief Create elements equal (on same nodes) to given ones
10496  *  \param [in] theElements - a set of elems to duplicate. If it is empty, all
10497  *              elements of the uppest dimension are duplicated.
10498  */
10499 //================================================================================
10500
10501 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
10502 {
10503   ClearLastCreated();
10504   SMESHDS_Mesh* mesh = GetMeshDS();
10505
10506   // get an element type and an iterator over elements
10507
10508   SMDSAbs_ElementType type = SMDSAbs_All;
10509   SMDS_ElemIteratorPtr elemIt;
10510   vector< const SMDS_MeshElement* > allElems;
10511   if ( theElements.empty() )
10512   {
10513     if ( mesh->NbNodes() == 0 )
10514       return;
10515     // get most complex type
10516     SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10517       SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10518       SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10519     };
10520     for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10521       if ( mesh->GetMeshInfo().NbElements( types[i] ))
10522       {
10523         type = types[i];
10524         break;
10525       }
10526     // put all elements in the vector <allElems>
10527     allElems.reserve( mesh->GetMeshInfo().NbElements( type ));
10528     elemIt = mesh->elementsIterator( type );
10529     while ( elemIt->more() )
10530       allElems.push_back( elemIt->next());
10531     elemIt = elemSetIterator( allElems );
10532   }
10533   else
10534   {
10535     type = (*theElements.begin())->GetType();
10536     elemIt = elemSetIterator( theElements );
10537   }
10538
10539   // duplicate elements
10540
10541   ElemFeatures elemType;
10542
10543   vector< const SMDS_MeshNode* > nodes;
10544   while ( elemIt->more() )
10545   {
10546     const SMDS_MeshElement* elem = elemIt->next();
10547     if ( elem->GetType() != type )
10548       continue;
10549
10550     elemType.Init( elem, /*basicOnly=*/false );
10551     nodes.assign( elem->begin_nodes(), elem->end_nodes() );
10552
10553     AddElement( nodes, elemType );
10554   }
10555 }
10556
10557 //================================================================================
10558 /*!
10559   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10560   \param theElems - the list of elements (edges or faces) to be replicated
10561   The nodes for duplication could be found from these elements
10562   \param theNodesNot - list of nodes to NOT replicate
10563   \param theAffectedElems - the list of elements (cells and edges) to which the
10564   replicated nodes should be associated to.
10565   \return TRUE if operation has been completed successfully, FALSE otherwise
10566 */
10567 //================================================================================
10568
10569 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10570                                     const TIDSortedElemSet& theNodesNot,
10571                                     const TIDSortedElemSet& theAffectedElems )
10572 {
10573   myLastCreatedElems.Clear();
10574   myLastCreatedNodes.Clear();
10575
10576   if ( theElems.size() == 0 )
10577     return false;
10578
10579   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10580   if ( !aMeshDS )
10581     return false;
10582
10583   bool res = false;
10584   TNodeNodeMap anOldNodeToNewNode;
10585   // duplicate elements and nodes
10586   res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10587   // replce nodes by duplications
10588   res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10589   return res;
10590 }
10591
10592 //================================================================================
10593 /*!
10594   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10595   \param theMeshDS - mesh instance
10596   \param theElems - the elements replicated or modified (nodes should be changed)
10597   \param theNodesNot - nodes to NOT replicate
10598   \param theNodeNodeMap - relation of old node to new created node
10599   \param theIsDoubleElem - flag os to replicate element or modify
10600   \return TRUE if operation has been completed successfully, FALSE otherwise
10601 */
10602 //================================================================================
10603
10604 bool SMESH_MeshEditor::doubleNodes(SMESHDS_Mesh*           theMeshDS,
10605                                    const TIDSortedElemSet& theElems,
10606                                    const TIDSortedElemSet& theNodesNot,
10607                                    TNodeNodeMap&           theNodeNodeMap,
10608                                    const bool              theIsDoubleElem )
10609 {
10610   // iterate through element and duplicate them (by nodes duplication)
10611   bool res = false;
10612   std::vector<const SMDS_MeshNode*> newNodes;
10613   ElemFeatures elemType;
10614
10615   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10616   for ( ;  elemItr != theElems.end(); ++elemItr )
10617   {
10618     const SMDS_MeshElement* anElem = *elemItr;
10619     if (!anElem)
10620       continue;
10621
10622     // duplicate nodes to duplicate element
10623     bool isDuplicate = false;
10624     newNodes.resize( anElem->NbNodes() );
10625     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10626     int ind = 0;
10627     while ( anIter->more() )
10628     {
10629       const SMDS_MeshNode* aCurrNode = static_cast<const SMDS_MeshNode*>( anIter->next() );
10630       const SMDS_MeshNode*  aNewNode = aCurrNode;
10631       TNodeNodeMap::iterator     n2n = theNodeNodeMap.find( aCurrNode );
10632       if ( n2n != theNodeNodeMap.end() )
10633       {
10634         aNewNode = n2n->second;
10635       }
10636       else if ( theIsDoubleElem && !theNodesNot.count( aCurrNode ))
10637       {
10638         // duplicate node
10639         aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10640         copyPosition( aCurrNode, aNewNode );
10641         theNodeNodeMap[ aCurrNode ] = aNewNode;
10642         myLastCreatedNodes.Append( aNewNode );
10643       }
10644       isDuplicate |= (aCurrNode != aNewNode);
10645       newNodes[ ind++ ] = aNewNode;
10646     }
10647     if ( !isDuplicate )
10648       continue;
10649
10650     if ( theIsDoubleElem )
10651       AddElement( newNodes, elemType.Init( anElem, /*basicOnly=*/false ));
10652     else
10653       theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], newNodes.size() );
10654
10655     res = true;
10656   }
10657   return res;
10658 }
10659
10660 //================================================================================
10661 /*!
10662   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10663   \param theNodes - identifiers of nodes to be doubled
10664   \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10665   nodes. If list of element identifiers is empty then nodes are doubled but
10666   they not assigned to elements
10667   \return TRUE if operation has been completed successfully, FALSE otherwise
10668 */
10669 //================================================================================
10670
10671 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10672                                     const std::list< int >& theListOfModifiedElems )
10673 {
10674   myLastCreatedElems.Clear();
10675   myLastCreatedNodes.Clear();
10676
10677   if ( theListOfNodes.size() == 0 )
10678     return false;
10679
10680   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10681   if ( !aMeshDS )
10682     return false;
10683
10684   // iterate through nodes and duplicate them
10685
10686   std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10687
10688   std::list< int >::const_iterator aNodeIter;
10689   for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10690   {
10691     int aCurr = *aNodeIter;
10692     SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr );
10693     if ( !aNode )
10694       continue;
10695
10696     // duplicate node
10697
10698     const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10699     if ( aNewNode )
10700     {
10701       copyPosition( aNode, aNewNode );
10702       anOldNodeToNewNode[ aNode ] = aNewNode;
10703       myLastCreatedNodes.Append( aNewNode );
10704     }
10705   }
10706
10707   // Create map of new nodes for modified elements
10708
10709   std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> > anElemToNodes;
10710
10711   std::list< int >::const_iterator anElemIter;
10712   for ( anElemIter = theListOfModifiedElems.begin();
10713         anElemIter != theListOfModifiedElems.end(); ++anElemIter )
10714   {
10715     int aCurr = *anElemIter;
10716     SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr );
10717     if ( !anElem )
10718       continue;
10719
10720     vector<const SMDS_MeshNode*> aNodeArr( anElem->NbNodes() );
10721
10722     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10723     int ind = 0;
10724     while ( anIter->more() )
10725     {
10726       SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10727       if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() )
10728       {
10729         const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ];
10730         aNodeArr[ ind++ ] = aNewNode;
10731       }
10732       else
10733         aNodeArr[ ind++ ] = aCurrNode;
10734     }
10735     anElemToNodes[ anElem ] = aNodeArr;
10736   }
10737
10738   // Change nodes of elements
10739
10740   std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> >::iterator
10741     anElemToNodesIter = anElemToNodes.begin();
10742   for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter )
10743   {
10744     const SMDS_MeshElement* anElem = anElemToNodesIter->first;
10745     vector<const SMDS_MeshNode*> aNodeArr = anElemToNodesIter->second;
10746     if ( anElem )
10747     {
10748       aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() );
10749     }
10750   }
10751
10752   return true;
10753 }
10754
10755 namespace {
10756
10757   //================================================================================
10758   /*!
10759   \brief Check if element located inside shape
10760   \return TRUE if IN or ON shape, FALSE otherwise
10761   */
10762   //================================================================================
10763
10764   template<class Classifier>
10765   bool isInside(const SMDS_MeshElement* theElem,
10766                 Classifier&             theClassifier,
10767                 const double            theTol)
10768   {
10769     gp_XYZ centerXYZ (0, 0, 0);
10770     SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
10771     while (aNodeItr->more())
10772       centerXYZ += SMESH_TNodeXYZ(cast2Node( aNodeItr->next()));
10773
10774     gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10775     theClassifier.Perform(aPnt, theTol);
10776     TopAbs_State aState = theClassifier.State();
10777     return (aState == TopAbs_IN || aState == TopAbs_ON );
10778   }
10779
10780   //================================================================================
10781   /*!
10782    * \brief Classifier of the 3D point on the TopoDS_Face
10783    *        with interaface suitable for isInside()
10784    */
10785   //================================================================================
10786
10787   struct _FaceClassifier
10788   {
10789     Extrema_ExtPS       _extremum;
10790     BRepAdaptor_Surface _surface;
10791     TopAbs_State        _state;
10792
10793     _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10794     {
10795       _extremum.Initialize( _surface,
10796                             _surface.FirstUParameter(), _surface.LastUParameter(),
10797                             _surface.FirstVParameter(), _surface.LastVParameter(),
10798                             _surface.Tolerance(), _surface.Tolerance() );
10799     }
10800     void Perform(const gp_Pnt& aPnt, double theTol)
10801     {
10802       theTol *= theTol;
10803       _state = TopAbs_OUT;
10804       _extremum.Perform(aPnt);
10805       if ( _extremum.IsDone() )
10806         for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10807           _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10808     }
10809     TopAbs_State State() const
10810     {
10811       return _state;
10812     }
10813   };
10814 }
10815
10816 //================================================================================
10817 /*!
10818   \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
10819   This method is the first step of DoubleNodeElemGroupsInRegion.
10820   \param theElems - list of groups of elements (edges or faces) to be replicated
10821   \param theNodesNot - list of groups of nodes not to replicated
10822   \param theShape - shape to detect affected elements (element which geometric center
10823          located on or inside shape). If the shape is null, detection is done on faces orientations
10824          (select elements with a gravity center on the side given by faces normals).
10825          This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
10826          The replicated nodes should be associated to affected elements.
10827   \return groups of affected elements
10828   \sa DoubleNodeElemGroupsInRegion()
10829  */
10830 //================================================================================
10831
10832 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
10833                                                    const TIDSortedElemSet& theNodesNot,
10834                                                    const TopoDS_Shape&     theShape,
10835                                                    TIDSortedElemSet&       theAffectedElems)
10836 {
10837   if ( theShape.IsNull() )
10838   {
10839     std::set<const SMDS_MeshNode*> alreadyCheckedNodes;
10840     std::set<const SMDS_MeshElement*> alreadyCheckedElems;
10841     std::set<const SMDS_MeshElement*> edgesToCheck;
10842     alreadyCheckedNodes.clear();
10843     alreadyCheckedElems.clear();
10844     edgesToCheck.clear();
10845
10846     // --- iterates on elements to be replicated and get elements by back references from their nodes
10847
10848     TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10849     for ( ;  elemItr != theElems.end(); ++elemItr )
10850     {
10851       SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10852       if (!anElem || (anElem->GetType() != SMDSAbs_Face))
10853         continue;
10854       gp_XYZ normal;
10855       SMESH_MeshAlgos::FaceNormal( anElem, normal, /*normalized=*/true );
10856       std::set<const SMDS_MeshNode*> nodesElem;
10857       nodesElem.clear();
10858       SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10859       while ( nodeItr->more() )
10860       {
10861         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10862         nodesElem.insert(aNode);
10863       }
10864       std::set<const SMDS_MeshNode*>::iterator nodit = nodesElem.begin();
10865       for (; nodit != nodesElem.end(); nodit++)
10866       {
10867         const SMDS_MeshNode* aNode = *nodit;
10868         if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10869           continue;
10870         if (alreadyCheckedNodes.find(aNode) != alreadyCheckedNodes.end())
10871           continue;
10872         alreadyCheckedNodes.insert(aNode);
10873         SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10874         while ( backElemItr->more() )
10875         {
10876           const SMDS_MeshElement* curElem = backElemItr->next();
10877           if (alreadyCheckedElems.find(curElem) != alreadyCheckedElems.end())
10878             continue;
10879           if (theElems.find(curElem) != theElems.end())
10880             continue;
10881           alreadyCheckedElems.insert(curElem);
10882           double x=0, y=0, z=0;
10883           int nb = 0;
10884           SMDS_ElemIteratorPtr nodeItr2 = curElem->nodesIterator();
10885           while ( nodeItr2->more() )
10886           {
10887             const SMDS_MeshNode* anotherNode = cast2Node(nodeItr2->next());
10888             x += anotherNode->X();
10889             y += anotherNode->Y();
10890             z += anotherNode->Z();
10891             nb++;
10892           }
10893           gp_XYZ p;
10894           p.SetCoord( x/nb -aNode->X(),
10895                       y/nb -aNode->Y(),
10896                       z/nb -aNode->Z() );
10897           if (normal*p > 0)
10898           {
10899             theAffectedElems.insert( curElem );
10900           }
10901           else if (curElem->GetType() == SMDSAbs_Edge)
10902             edgesToCheck.insert(curElem);
10903         }
10904       }
10905     }
10906     // --- add also edges lying on the set of faces (all nodes in alreadyCheckedNodes)
10907     std::set<const SMDS_MeshElement*>::iterator eit = edgesToCheck.begin();
10908     for( ; eit != edgesToCheck.end(); eit++)
10909     {
10910       bool onside = true;
10911       const SMDS_MeshElement* anEdge = *eit;
10912       SMDS_ElemIteratorPtr nodeItr = anEdge->nodesIterator();
10913       while ( nodeItr->more() )
10914       {
10915         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10916         if (alreadyCheckedNodes.find(aNode) == alreadyCheckedNodes.end())
10917         {
10918           onside = false;
10919           break;
10920         }
10921       }
10922       if (onside)
10923       {
10924         theAffectedElems.insert(anEdge);
10925       }
10926     }
10927   }
10928   else
10929   {
10930     const double aTol = Precision::Confusion();
10931     auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
10932     auto_ptr<_FaceClassifier>              aFaceClassifier;
10933     if ( theShape.ShapeType() == TopAbs_SOLID )
10934     {
10935       bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10936       bsc3d->PerformInfinitePoint(aTol);
10937     }
10938     else if (theShape.ShapeType() == TopAbs_FACE )
10939     {
10940       aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10941     }
10942
10943     // iterates on indicated elements and get elements by back references from their nodes
10944     TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10945     for ( ;  elemItr != theElems.end(); ++elemItr )
10946     {
10947       SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10948       if (!anElem)
10949         continue;
10950       SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10951       while ( nodeItr->more() )
10952       {
10953         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10954         if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10955           continue;
10956         SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10957         while ( backElemItr->more() )
10958         {
10959           const SMDS_MeshElement* curElem = backElemItr->next();
10960           if ( curElem && theElems.find(curElem) == theElems.end() &&
10961               ( bsc3d.get() ?
10962                 isInside( curElem, *bsc3d, aTol ) :
10963                 isInside( curElem, *aFaceClassifier, aTol )))
10964             theAffectedElems.insert( curElem );
10965         }
10966       }
10967     }
10968   }
10969   return true;
10970 }
10971
10972 //================================================================================
10973 /*!
10974   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10975   \param theElems - group of of elements (edges or faces) to be replicated
10976   \param theNodesNot - group of nodes not to replicate
10977   \param theShape - shape to detect affected elements (element which geometric center
10978   located on or inside shape).
10979   The replicated nodes should be associated to affected elements.
10980   \return TRUE if operation has been completed successfully, FALSE otherwise
10981 */
10982 //================================================================================
10983
10984 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
10985                                             const TIDSortedElemSet& theNodesNot,
10986                                             const TopoDS_Shape&     theShape )
10987 {
10988   if ( theShape.IsNull() )
10989     return false;
10990
10991   const double aTol = Precision::Confusion();
10992   SMESHUtils::Deleter< BRepClass3d_SolidClassifier> bsc3d;
10993   SMESHUtils::Deleter<_FaceClassifier>              aFaceClassifier;
10994   if ( theShape.ShapeType() == TopAbs_SOLID )
10995   {
10996     bsc3d._obj = new BRepClass3d_SolidClassifier( theShape );
10997     bsc3d->PerformInfinitePoint(aTol);
10998   }
10999   else if (theShape.ShapeType() == TopAbs_FACE )
11000   {
11001     aFaceClassifier._obj = new _FaceClassifier( TopoDS::Face( theShape ));
11002   }
11003
11004   // iterates on indicated elements and get elements by back references from their nodes
11005   TIDSortedElemSet anAffected;
11006   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11007   for ( ;  elemItr != theElems.end(); ++elemItr )
11008   {
11009     SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11010     if (!anElem)
11011       continue;
11012
11013     SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11014     while ( nodeItr->more() )
11015     {
11016       const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11017       if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11018         continue;
11019       SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11020       while ( backElemItr->more() )
11021       {
11022         const SMDS_MeshElement* curElem = backElemItr->next();
11023         if ( curElem && theElems.find(curElem) == theElems.end() &&
11024              ( bsc3d ?
11025                isInside( curElem, *bsc3d, aTol ) :
11026                isInside( curElem, *aFaceClassifier, aTol )))
11027           anAffected.insert( curElem );
11028       }
11029     }
11030   }
11031   return DoubleNodes( theElems, theNodesNot, anAffected );
11032 }
11033
11034 /*!
11035  *  \brief compute an oriented angle between two planes defined by four points.
11036  *  The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
11037  *  @param p0 base of the rotation axe
11038  *  @param p1 extremity of the rotation axe
11039  *  @param g1 belongs to the first plane
11040  *  @param g2 belongs to the second plane
11041  */
11042 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
11043 {
11044   gp_Vec vref(p0, p1);
11045   gp_Vec v1(p0, g1);
11046   gp_Vec v2(p0, g2);
11047   gp_Vec n1 = vref.Crossed(v1);
11048   gp_Vec n2 = vref.Crossed(v2);
11049   try {
11050     return n2.AngleWithRef(n1, vref);
11051   }
11052   catch ( Standard_Failure ) {
11053   }
11054   return Max( v1.Magnitude(), v2.Magnitude() );
11055 }
11056
11057 /*!
11058  * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11059  *  The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
11060  * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
11061  * Triangles are transformed into prisms, and quadrangles into hexahedrons.
11062  * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
11063  * 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.
11064  * 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.
11065  * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
11066  * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
11067  * \param theElems - list of groups of volumes, where a group of volume is a set of
11068  *        SMDS_MeshElements sorted by Id.
11069  * \param createJointElems - if TRUE, create the elements
11070  * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
11071  *        the boundary between \a theDomains and the rest mesh
11072  * \return TRUE if operation has been completed successfully, FALSE otherwise
11073  */
11074 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11075                                                      bool                                 createJointElems,
11076                                                      bool                                 onAllBoundaries)
11077 {
11078   MESSAGE("----------------------------------------------");
11079   MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11080   MESSAGE("----------------------------------------------");
11081
11082   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11083   meshDS->BuildDownWardConnectivity(true);
11084   CHRONO(50);
11085   SMDS_UnstructuredGrid *grid = meshDS->getGrid();
11086
11087   // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11088   //     build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11089   //     build the list of nodes shared by 2 or more domains, with their domain indexes
11090
11091   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11092   std::map<int,int>celldom; // cell vtkId --> domain
11093   std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains;  // oldNode --> (id domain --> id cell)
11094   std::map<int, std::map<int,int> > nodeDomains; // oldId -->  (domainId --> newId)
11095   faceDomains.clear();
11096   celldom.clear();
11097   cellDomains.clear();
11098   nodeDomains.clear();
11099   std::map<int,int> emptyMap;
11100   std::set<int> emptySet;
11101   emptyMap.clear();
11102
11103   MESSAGE(".. Number of domains :"<<theElems.size());
11104
11105   TIDSortedElemSet theRestDomElems;
11106   const int iRestDom  = -1;
11107   const int idom0     = onAllBoundaries ? iRestDom : 0;
11108   const int nbDomains = theElems.size();
11109
11110   // Check if the domains do not share an element
11111   for (int idom = 0; idom < nbDomains-1; idom++)
11112   {
11113     //       MESSAGE("... Check of domain #" << idom);
11114     const TIDSortedElemSet& domain = theElems[idom];
11115     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11116     for (; elemItr != domain.end(); ++elemItr)
11117     {
11118       const SMDS_MeshElement* anElem = *elemItr;
11119       int idombisdeb = idom + 1 ;
11120       // check if the element belongs to a domain further in the list
11121       for ( size_t idombis = idombisdeb; idombis < theElems.size(); idombis++ )
11122       {
11123         const TIDSortedElemSet& domainbis = theElems[idombis];
11124         if ( domainbis.count( anElem ))
11125         {
11126           MESSAGE(".... Domain #" << idom);
11127           MESSAGE(".... Domain #" << idombis);
11128           throw SALOME_Exception("The domains are not disjoint.");
11129           return false ;
11130         }
11131       }
11132     }
11133   }
11134
11135   for (int idom = 0; idom < nbDomains; idom++)
11136   {
11137
11138     // --- build a map (face to duplicate --> volume to modify)
11139     //     with all the faces shared by 2 domains (group of elements)
11140     //     and corresponding volume of this domain, for each shared face.
11141     //     a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11142
11143     MESSAGE("... Neighbors of domain #" << idom);
11144     const TIDSortedElemSet& domain = theElems[idom];
11145     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11146     for (; elemItr != domain.end(); ++elemItr)
11147     {
11148       const SMDS_MeshElement* anElem = *elemItr;
11149       if (!anElem)
11150         continue;
11151       int vtkId = anElem->getVtkId();
11152       //MESSAGE("  vtkId " << vtkId << " smdsId " << anElem->GetID());
11153       int neighborsVtkIds[NBMAXNEIGHBORS];
11154       int downIds[NBMAXNEIGHBORS];
11155       unsigned char downTypes[NBMAXNEIGHBORS];
11156       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11157       for (int n = 0; n < nbNeighbors; n++)
11158       {
11159         int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
11160         const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11161         if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
11162         {
11163           bool ok = false;
11164           for ( size_t idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
11165           {
11166             // MESSAGE("Domain " << idombis);
11167             const TIDSortedElemSet& domainbis = theElems[idombis];
11168             if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11169           }
11170           if ( ok || onAllBoundaries ) // the characteristics of the face is stored
11171           {
11172             DownIdType face(downIds[n], downTypes[n]);
11173             if (!faceDomains[face].count(idom))
11174             {
11175               faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11176               celldom[vtkId] = idom;
11177               //MESSAGE("       cell with a border " << vtkId << " domain " << idom);
11178             }
11179             if ( !ok )
11180             {
11181               theRestDomElems.insert( elem );
11182               faceDomains[face][iRestDom] = neighborsVtkIds[n];
11183               celldom[neighborsVtkIds[n]] = iRestDom;
11184             }
11185           }
11186         }
11187       }
11188     }
11189   }
11190
11191   //MESSAGE("Number of shared faces " << faceDomains.size());
11192   std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11193
11194   // --- explore the shared faces domain by domain,
11195   //     explore the nodes of the face and see if they belong to a cell in the domain,
11196   //     which has only a node or an edge on the border (not a shared face)
11197
11198   for (int idomain = idom0; idomain < nbDomains; idomain++)
11199   {
11200     //MESSAGE("Domain " << idomain);
11201     const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
11202     itface = faceDomains.begin();
11203     for (; itface != faceDomains.end(); ++itface)
11204     {
11205       const std::map<int, int>& domvol = itface->second;
11206       if (!domvol.count(idomain))
11207         continue;
11208       DownIdType face = itface->first;
11209       //MESSAGE(" --- face " << face.cellId);
11210       std::set<int> oldNodes;
11211       oldNodes.clear();
11212       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11213       std::set<int>::iterator itn = oldNodes.begin();
11214       for (; itn != oldNodes.end(); ++itn)
11215       {
11216         int oldId = *itn;
11217         //MESSAGE("     node " << oldId);
11218         vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11219         for (int i=0; i<l.ncells; i++)
11220         {
11221           int vtkId = l.cells[i];
11222           const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
11223           if (!domain.count(anElem))
11224             continue;
11225           int vtkType = grid->GetCellType(vtkId);
11226           int downId = grid->CellIdToDownId(vtkId);
11227           if (downId < 0)
11228           {
11229             MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11230             continue; // not OK at this stage of the algorithm:
11231             //no cells created after BuildDownWardConnectivity
11232           }
11233           DownIdType aCell(downId, vtkType);
11234           cellDomains[aCell][idomain] = vtkId;
11235           celldom[vtkId] = idomain;
11236           //MESSAGE("       cell " << vtkId << " domain " << idomain);
11237         }
11238       }
11239     }
11240   }
11241
11242   // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11243   //     for each shared face, get the nodes
11244   //     for each node, for each domain of the face, create a clone of the node
11245
11246   // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11247   //     junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11248   //     the value is the ordered domain ids. (more than 4 domains not taken into account)
11249
11250   std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11251   std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11252   std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11253
11254   MESSAGE(".. Duplication of the nodes");
11255   for (int idomain = idom0; idomain < nbDomains; idomain++)
11256   {
11257     itface = faceDomains.begin();
11258     for (; itface != faceDomains.end(); ++itface)
11259     {
11260       const std::map<int, int>& domvol = itface->second;
11261       if (!domvol.count(idomain))
11262         continue;
11263       DownIdType face = itface->first;
11264       //MESSAGE(" --- face " << face.cellId);
11265       std::set<int> oldNodes;
11266       oldNodes.clear();
11267       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11268       std::set<int>::iterator itn = oldNodes.begin();
11269       for (; itn != oldNodes.end(); ++itn)
11270       {
11271         int oldId = *itn;
11272         if (nodeDomains[oldId].empty())
11273         {
11274           nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11275           //MESSAGE("-+-+-b     oldNode " << oldId << " domain " << idomain);
11276         }
11277         std::map<int, int>::const_iterator itdom = domvol.begin();
11278         for (; itdom != domvol.end(); ++itdom)
11279         {
11280           int idom = itdom->first;
11281           //MESSAGE("         domain " << idom);
11282           if (!nodeDomains[oldId].count(idom)) // --- node to clone
11283           {
11284             if (nodeDomains[oldId].size() >= 2) // a multiple node
11285             {
11286               vector<int> orderedDoms;
11287               //MESSAGE("multiple node " << oldId);
11288               if (mutipleNodes.count(oldId))
11289                 orderedDoms = mutipleNodes[oldId];
11290               else
11291               {
11292                 map<int,int>::iterator it = nodeDomains[oldId].begin();
11293                 for (; it != nodeDomains[oldId].end(); ++it)
11294                   orderedDoms.push_back(it->first);
11295               }
11296               orderedDoms.push_back(idom); // TODO order ==> push_front or back
11297               //stringstream txt;
11298               //for (int i=0; i<orderedDoms.size(); i++)
11299               //  txt << orderedDoms[i] << " ";
11300               //MESSAGE("orderedDoms " << txt.str());
11301               mutipleNodes[oldId] = orderedDoms;
11302             }
11303             double *coords = grid->GetPoint(oldId);
11304             SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11305             copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11306             int newId = newNode->getVtkId();
11307             nodeDomains[oldId][idom] = newId; // cloned node for other domains
11308             //MESSAGE("-+-+-c     oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11309           }
11310         }
11311       }
11312     }
11313   }
11314
11315   MESSAGE(".. Creation of elements");
11316   for (int idomain = idom0; idomain < nbDomains; idomain++)
11317   {
11318     itface = faceDomains.begin();
11319     for (; itface != faceDomains.end(); ++itface)
11320     {
11321       std::map<int, int> domvol = itface->second;
11322       if (!domvol.count(idomain))
11323         continue;
11324       DownIdType face = itface->first;
11325       //MESSAGE(" --- face " << face.cellId);
11326       std::set<int> oldNodes;
11327       oldNodes.clear();
11328       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11329       int nbMultipleNodes = 0;
11330       std::set<int>::iterator itn = oldNodes.begin();
11331       for (; itn != oldNodes.end(); ++itn)
11332       {
11333         int oldId = *itn;
11334         if (mutipleNodes.count(oldId))
11335           nbMultipleNodes++;
11336       }
11337       if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11338       {
11339         //MESSAGE("multiple Nodes detected on a shared face");
11340         int downId = itface->first.cellId;
11341         unsigned char cellType = itface->first.cellType;
11342         // --- shared edge or shared face ?
11343         if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11344         {
11345           int nodes[3];
11346           int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11347           for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11348             if (mutipleNodes.count(nodes[i]))
11349               if (!mutipleNodesToFace.count(nodes[i]))
11350                 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11351         }
11352         else // shared face (between two volumes)
11353         {
11354           int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11355           const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11356           const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11357           for (int ie =0; ie < nbEdges; ie++)
11358           {
11359             int nodes[3];
11360             int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11361             if ( mutipleNodes.count(nodes[0]) && mutipleNodes.count( nodes[ nbNodes-1 ]))
11362             {
11363               vector<int> vn0 = mutipleNodes[nodes[0]];
11364               vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11365               vector<int> doms;
11366               for ( size_t i0 = 0; i0 < vn0.size(); i0++ )
11367                 for ( size_t i1 = 0; i1 < vn1.size(); i1++ )
11368                   if ( vn0[i0] == vn1[i1] )
11369                     doms.push_back( vn0[ i0 ]);
11370               if ( doms.size() > 2 )
11371               {
11372                 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11373                 double *coords = grid->GetPoint(nodes[0]);
11374                 gp_Pnt p0(coords[0], coords[1], coords[2]);
11375                 coords = grid->GetPoint(nodes[nbNodes - 1]);
11376                 gp_Pnt p1(coords[0], coords[1], coords[2]);
11377                 gp_Pnt gref;
11378                 int vtkVolIds[1000];  // an edge can belong to a lot of volumes
11379                 map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
11380                 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11381                 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11382                 for ( size_t id = 0; id < doms.size(); id++ )
11383                 {
11384                   int idom = doms[id];
11385                   const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11386                   for ( int ivol = 0; ivol < nbvol; ivol++ )
11387                   {
11388                     int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
11389                     SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
11390                     if (domain.count(elem))
11391                     {
11392                       SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
11393                       domvol[idom] = svol;
11394                       //MESSAGE("  domain " << idom << " volume " << elem->GetID());
11395                       double values[3];
11396                       vtkIdType npts = 0;
11397                       vtkIdType* pts = 0;
11398                       grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11399                       SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
11400                       if (id ==0)
11401                       {
11402                         gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
11403                         angleDom[idom] = 0;
11404                       }
11405                       else
11406                       {
11407                         gp_Pnt g(values[0], values[1], values[2]);
11408                         angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11409                         //MESSAGE("  angle=" << angleDom[idom]);
11410                       }
11411                       break;
11412                     }
11413                   }
11414                 }
11415                 map<double, int> sortedDom; // sort domains by angle
11416                 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11417                   sortedDom[ia->second] = ia->first;
11418                 vector<int> vnodes;
11419                 vector<int> vdom;
11420                 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11421                 {
11422                   vdom.push_back(ib->second);
11423                   //MESSAGE("  ordered domain " << ib->second << "  angle " << ib->first);
11424                 }
11425                 for (int ino = 0; ino < nbNodes; ino++)
11426                   vnodes.push_back(nodes[ino]);
11427                 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11428               }
11429             }
11430           }
11431         }
11432       }
11433     }
11434   }
11435
11436   // --- iterate on shared faces (volumes to modify, face to extrude)
11437   //     get node id's of the face (id SMDS = id VTK)
11438   //     create flat element with old and new nodes if requested
11439
11440   // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11441   //     (domain1 X domain2) = domain1 + MAXINT*domain2
11442
11443   std::map<int, std::map<long,int> > nodeQuadDomains;
11444   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11445
11446   MESSAGE(".. Creation of elements: simple junction");
11447   if (createJointElems)
11448   {
11449     int idg;
11450     string joints2DName = "joints2D";
11451     mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
11452     SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11453     string joints3DName = "joints3D";
11454     mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
11455     SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11456
11457     itface = faceDomains.begin();
11458     for (; itface != faceDomains.end(); ++itface)
11459     {
11460       DownIdType face = itface->first;
11461       std::set<int> oldNodes;
11462       std::set<int>::iterator itn;
11463       oldNodes.clear();
11464       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11465
11466       std::map<int, int> domvol = itface->second;
11467       std::map<int, int>::iterator itdom = domvol.begin();
11468       int dom1 = itdom->first;
11469       int vtkVolId = itdom->second;
11470       itdom++;
11471       int dom2 = itdom->first;
11472       SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11473                                                        nodeQuadDomains);
11474       stringstream grpname;
11475       grpname << "j_";
11476       if (dom1 < dom2)
11477         grpname << dom1 << "_" << dom2;
11478       else
11479         grpname << dom2 << "_" << dom1;
11480       string namegrp = grpname.str();
11481       if (!mapOfJunctionGroups.count(namegrp))
11482         mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
11483       SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11484       if (sgrp)
11485         sgrp->Add(vol->GetID());
11486       if (vol->GetType() == SMDSAbs_Volume)
11487         joints3DGrp->Add(vol->GetID());
11488       else if (vol->GetType() == SMDSAbs_Face)
11489         joints2DGrp->Add(vol->GetID());
11490     }
11491   }
11492
11493   // --- create volumes on multiple domain intersection if requested
11494   //     iterate on mutipleNodesToFace
11495   //     iterate on edgesMultiDomains
11496
11497   MESSAGE(".. Creation of elements: multiple junction");
11498   if (createJointElems)
11499   {
11500     // --- iterate on mutipleNodesToFace
11501
11502     std::map<int, std::vector<int> >::iterator itn =  mutipleNodesToFace.begin();
11503     for (; itn != mutipleNodesToFace.end(); ++itn)
11504     {
11505       int node = itn->first;
11506       vector<int> orderDom = itn->second;
11507       vector<vtkIdType> orderedNodes;
11508       for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11509         orderedNodes.push_back( nodeDomains[ node ][ orderDom[ idom ]]);
11510       SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11511
11512       stringstream grpname;
11513       grpname << "m2j_";
11514       grpname << 0 << "_" << 0;
11515       int idg;
11516       string namegrp = grpname.str();
11517       if (!mapOfJunctionGroups.count(namegrp))
11518         mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
11519       SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11520       if (sgrp)
11521         sgrp->Add(face->GetID());
11522     }
11523
11524     // --- iterate on edgesMultiDomains
11525
11526     std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11527     for (; ite != edgesMultiDomains.end(); ++ite)
11528     {
11529       vector<int> nodes = ite->first;
11530       vector<int> orderDom = ite->second;
11531       vector<vtkIdType> orderedNodes;
11532       if (nodes.size() == 2)
11533       {
11534         //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11535         for ( size_t ino = 0; ino < nodes.size(); ino++ )
11536           if ( orderDom.size() == 3 )
11537             for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11538               orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11539           else
11540             for (int idom = orderDom.size()-1; idom >=0; idom--)
11541               orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11542         SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11543
11544         int idg;
11545         string namegrp = "jointsMultiples";
11546         if (!mapOfJunctionGroups.count(namegrp))
11547           mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11548         SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11549         if (sgrp)
11550           sgrp->Add(vol->GetID());
11551       }
11552       else
11553       {
11554         //INFOS("Quadratic multiple joints not implemented");
11555         // TODO quadratic nodes
11556       }
11557     }
11558   }
11559
11560   // --- list the explicit faces and edges of the mesh that need to be modified,
11561   //     i.e. faces and edges built with one or more duplicated nodes.
11562   //     associate these faces or edges to their corresponding domain.
11563   //     only the first domain found is kept when a face or edge is shared
11564
11565   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11566   std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11567   faceOrEdgeDom.clear();
11568   feDom.clear();
11569
11570   MESSAGE(".. Modification of elements");
11571   for (int idomain = idom0; idomain < nbDomains; idomain++)
11572   {
11573     std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11574     for (; itnod != nodeDomains.end(); ++itnod)
11575     {
11576       int oldId = itnod->first;
11577       //MESSAGE("     node " << oldId);
11578       vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11579       for (int i = 0; i < l.ncells; i++)
11580       {
11581         int vtkId = l.cells[i];
11582         int vtkType = grid->GetCellType(vtkId);
11583         int downId = grid->CellIdToDownId(vtkId);
11584         if (downId < 0)
11585           continue; // new cells: not to be modified
11586         DownIdType aCell(downId, vtkType);
11587         int volParents[1000];
11588         int nbvol = grid->GetParentVolumes(volParents, vtkId);
11589         for (int j = 0; j < nbvol; j++)
11590           if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11591             if (!feDom.count(vtkId))
11592             {
11593               feDom[vtkId] = idomain;
11594               faceOrEdgeDom[aCell] = emptyMap;
11595               faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11596               //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
11597               //        << " type " << vtkType << " downId " << downId);
11598             }
11599       }
11600     }
11601   }
11602
11603   // --- iterate on shared faces (volumes to modify, face to extrude)
11604   //     get node id's of the face
11605   //     replace old nodes by new nodes in volumes, and update inverse connectivity
11606
11607   std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11608   for (int m=0; m<3; m++)
11609   {
11610     std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11611     itface = (*amap).begin();
11612     for (; itface != (*amap).end(); ++itface)
11613     {
11614       DownIdType face = itface->first;
11615       std::set<int> oldNodes;
11616       std::set<int>::iterator itn;
11617       oldNodes.clear();
11618       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11619       //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11620       std::map<int, int> localClonedNodeIds;
11621
11622       std::map<int, int> domvol = itface->second;
11623       std::map<int, int>::iterator itdom = domvol.begin();
11624       for (; itdom != domvol.end(); ++itdom)
11625       {
11626         int idom = itdom->first;
11627         int vtkVolId = itdom->second;
11628         //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
11629         localClonedNodeIds.clear();
11630         for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11631         {
11632           int oldId = *itn;
11633           if (nodeDomains[oldId].count(idom))
11634           {
11635             localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11636             //MESSAGE("     node " << oldId << " --> " << localClonedNodeIds[oldId]);
11637           }
11638         }
11639         meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11640       }
11641     }
11642   }
11643
11644   // Remove empty groups (issue 0022812)
11645   std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
11646   for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
11647   {
11648     if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
11649       myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
11650   }
11651
11652   meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11653   grid->BuildLinks();
11654
11655   CHRONOSTOP(50);
11656   counters::stats();
11657   return true;
11658 }
11659
11660 /*!
11661  * \brief Double nodes on some external faces and create flat elements.
11662  * Flat elements are mainly used by some types of mechanic calculations.
11663  *
11664  * Each group of the list must be constituted of faces.
11665  * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11666  * @param theElems - list of groups of faces, where a group of faces is a set of
11667  * SMDS_MeshElements sorted by Id.
11668  * @return TRUE if operation has been completed successfully, FALSE otherwise
11669  */
11670 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11671 {
11672   MESSAGE("-------------------------------------------------");
11673   MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11674   MESSAGE("-------------------------------------------------");
11675
11676   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11677
11678   // --- For each group of faces
11679   //     duplicate the nodes, create a flat element based on the face
11680   //     replace the nodes of the faces by their clones
11681
11682   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11683   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11684   clonedNodes.clear();
11685   intermediateNodes.clear();
11686   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11687   mapOfJunctionGroups.clear();
11688
11689   for ( size_t idom = 0; idom < theElems.size(); idom++ )
11690   {
11691     const TIDSortedElemSet&           domain = theElems[idom];
11692     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11693     for ( ; elemItr != domain.end(); ++elemItr )
11694     {
11695       SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11696       SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
11697       if (!aFace)
11698         continue;
11699       // MESSAGE("aFace=" << aFace->GetID());
11700       bool isQuad = aFace->IsQuadratic();
11701       vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11702
11703       // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11704
11705       SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
11706       while (nodeIt->more())
11707       {
11708         const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
11709         bool isMedium = isQuad && (aFace->IsMediumNode(node));
11710         if (isMedium)
11711           ln2.push_back(node);
11712         else
11713           ln0.push_back(node);
11714
11715         const SMDS_MeshNode* clone = 0;
11716         if (!clonedNodes.count(node))
11717         {
11718           clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11719           copyPosition( node, clone );
11720           clonedNodes[node] = clone;
11721         }
11722         else
11723           clone = clonedNodes[node];
11724
11725         if (isMedium)
11726           ln3.push_back(clone);
11727         else
11728           ln1.push_back(clone);
11729
11730         const SMDS_MeshNode* inter = 0;
11731         if (isQuad && (!isMedium))
11732         {
11733           if (!intermediateNodes.count(node))
11734           {
11735             inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11736             copyPosition( node, inter );
11737             intermediateNodes[node] = inter;
11738           }
11739           else
11740             inter = intermediateNodes[node];
11741           ln4.push_back(inter);
11742         }
11743       }
11744
11745       // --- extrude the face
11746
11747       vector<const SMDS_MeshNode*> ln;
11748       SMDS_MeshVolume* vol = 0;
11749       vtkIdType aType = aFace->GetVtkType();
11750       switch (aType)
11751       {
11752       case VTK_TRIANGLE:
11753         vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11754         // MESSAGE("vol prism " << vol->GetID());
11755         ln.push_back(ln1[0]);
11756         ln.push_back(ln1[1]);
11757         ln.push_back(ln1[2]);
11758         break;
11759       case VTK_QUAD:
11760         vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11761         // MESSAGE("vol hexa " << vol->GetID());
11762         ln.push_back(ln1[0]);
11763         ln.push_back(ln1[1]);
11764         ln.push_back(ln1[2]);
11765         ln.push_back(ln1[3]);
11766         break;
11767       case VTK_QUADRATIC_TRIANGLE:
11768         vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11769                                 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11770         // MESSAGE("vol quad prism " << vol->GetID());
11771         ln.push_back(ln1[0]);
11772         ln.push_back(ln1[1]);
11773         ln.push_back(ln1[2]);
11774         ln.push_back(ln3[0]);
11775         ln.push_back(ln3[1]);
11776         ln.push_back(ln3[2]);
11777         break;
11778       case VTK_QUADRATIC_QUAD:
11779         //              vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11780         //                                      ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11781         //                                      ln4[0], ln4[1], ln4[2], ln4[3]);
11782         vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11783                                 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11784                                 ln4[0], ln4[1], ln4[2], ln4[3]);
11785         // MESSAGE("vol quad hexa " << vol->GetID());
11786         ln.push_back(ln1[0]);
11787         ln.push_back(ln1[1]);
11788         ln.push_back(ln1[2]);
11789         ln.push_back(ln1[3]);
11790         ln.push_back(ln3[0]);
11791         ln.push_back(ln3[1]);
11792         ln.push_back(ln3[2]);
11793         ln.push_back(ln3[3]);
11794         break;
11795       case VTK_POLYGON:
11796         break;
11797       default:
11798         break;
11799       }
11800
11801       if (vol)
11802       {
11803         stringstream grpname;
11804         grpname << "jf_";
11805         grpname << idom;
11806         int idg;
11807         string namegrp = grpname.str();
11808         if (!mapOfJunctionGroups.count(namegrp))
11809           mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11810         SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11811         if (sgrp)
11812           sgrp->Add(vol->GetID());
11813       }
11814
11815       // --- modify the face
11816
11817       aFace->ChangeNodes(&ln[0], ln.size());
11818     }
11819   }
11820   return true;
11821 }
11822
11823 /*!
11824  *  \brief identify all the elements around a geom shape, get the faces delimiting the hole
11825  *  Build groups of volume to remove, groups of faces to replace on the skin of the object,
11826  *  groups of faces to remove inside the object, (idem edges).
11827  *  Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11828  */
11829 void SMESH_MeshEditor::CreateHoleSkin(double                          radius,
11830                                       const TopoDS_Shape&             theShape,
11831                                       SMESH_NodeSearcher*             theNodeSearcher,
11832                                       const char*                     groupName,
11833                                       std::vector<double>&            nodesCoords,
11834                                       std::vector<std::vector<int> >& listOfListOfNodes)
11835 {
11836   MESSAGE("--------------------------------");
11837   MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11838   MESSAGE("--------------------------------");
11839
11840   // --- zone of volumes to remove is given :
11841   //     1 either by a geom shape (one or more vertices) and a radius,
11842   //     2 either by a group of nodes (representative of the shape)to use with the radius,
11843   //     3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11844   //     In the case 2, the group of nodes is an external group of nodes from another mesh,
11845   //     In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11846   //     defined by it's name.
11847
11848   SMESHDS_GroupBase* groupDS = 0;
11849   SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11850   while ( groupIt->more() )
11851   {
11852     groupDS = 0;
11853     SMESH_Group * group = groupIt->next();
11854     if ( !group ) continue;
11855     groupDS = group->GetGroupDS();
11856     if ( !groupDS || groupDS->IsEmpty() ) continue;
11857     std::string grpName = group->GetName();
11858     //MESSAGE("grpName=" << grpName);
11859     if (grpName == groupName)
11860       break;
11861     else
11862       groupDS = 0;
11863   }
11864
11865   bool isNodeGroup = false;
11866   bool isNodeCoords = false;
11867   if (groupDS)
11868   {
11869     if (groupDS->GetType() != SMDSAbs_Node)
11870       return;
11871     isNodeGroup = true;     // a group of nodes exists and it is in this mesh
11872   }
11873
11874   if (nodesCoords.size() > 0)
11875     isNodeCoords = true; // a list o nodes given by their coordinates
11876   //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11877
11878   // --- define groups to build
11879
11880   int idg; // --- group of SMDS volumes
11881   string grpvName = groupName;
11882   grpvName += "_vol";
11883   SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
11884   if (!grp)
11885   {
11886     MESSAGE("group not created " << grpvName);
11887     return;
11888   }
11889   SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
11890
11891   int idgs; // --- group of SMDS faces on the skin
11892   string grpsName = groupName;
11893   grpsName += "_skin";
11894   SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
11895   if (!grps)
11896   {
11897     MESSAGE("group not created " << grpsName);
11898     return;
11899   }
11900   SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
11901
11902   int idgi; // --- group of SMDS faces internal (several shapes)
11903   string grpiName = groupName;
11904   grpiName += "_internalFaces";
11905   SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
11906   if (!grpi)
11907   {
11908     MESSAGE("group not created " << grpiName);
11909     return;
11910   }
11911   SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
11912
11913   int idgei; // --- group of SMDS faces internal (several shapes)
11914   string grpeiName = groupName;
11915   grpeiName += "_internalEdges";
11916   SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
11917   if (!grpei)
11918   {
11919     MESSAGE("group not created " << grpeiName);
11920     return;
11921   }
11922   SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
11923
11924   // --- build downward connectivity
11925
11926   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11927   meshDS->BuildDownWardConnectivity(true);
11928   SMDS_UnstructuredGrid* grid = meshDS->getGrid();
11929
11930   // --- set of volumes detected inside
11931
11932   std::set<int> setOfInsideVol;
11933   std::set<int> setOfVolToCheck;
11934
11935   std::vector<gp_Pnt> gpnts;
11936   gpnts.clear();
11937
11938   if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
11939   {
11940     MESSAGE("group of nodes provided");
11941     SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
11942     while ( elemIt->more() )
11943     {
11944       const SMDS_MeshElement* elem = elemIt->next();
11945       if (!elem)
11946         continue;
11947       const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
11948       if (!node)
11949         continue;
11950       SMDS_MeshElement* vol = 0;
11951       SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
11952       while (volItr->more())
11953       {
11954         vol = (SMDS_MeshElement*)volItr->next();
11955         setOfInsideVol.insert(vol->getVtkId());
11956         sgrp->Add(vol->GetID());
11957       }
11958     }
11959   }
11960   else if (isNodeCoords)
11961   {
11962     MESSAGE("list of nodes coordinates provided");
11963     size_t i = 0;
11964     int k = 0;
11965     while ( i < nodesCoords.size()-2 )
11966     {
11967       double x = nodesCoords[i++];
11968       double y = nodesCoords[i++];
11969       double z = nodesCoords[i++];
11970       gp_Pnt p = gp_Pnt(x, y ,z);
11971       gpnts.push_back(p);
11972       MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
11973       k++;
11974     }
11975   }
11976   else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
11977   {
11978     MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
11979     TopTools_IndexedMapOfShape vertexMap;
11980     TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
11981     gp_Pnt p = gp_Pnt(0,0,0);
11982     if (vertexMap.Extent() < 1)
11983       return;
11984
11985     for ( int i = 1; i <= vertexMap.Extent(); ++i )
11986     {
11987       const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
11988       p = BRep_Tool::Pnt(vertex);
11989       gpnts.push_back(p);
11990       MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
11991     }
11992   }
11993
11994   if (gpnts.size() > 0)
11995   {
11996     int nodeId = 0;
11997     const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
11998     if (startNode)
11999       nodeId = startNode->GetID();
12000     MESSAGE("nodeId " << nodeId);
12001
12002     double radius2 = radius*radius;
12003     MESSAGE("radius2 " << radius2);
12004
12005     // --- volumes on start node
12006
12007     setOfVolToCheck.clear();
12008     SMDS_MeshElement* startVol = 0;
12009     SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
12010     while (volItr->more())
12011     {
12012       startVol = (SMDS_MeshElement*)volItr->next();
12013       setOfVolToCheck.insert(startVol->getVtkId());
12014     }
12015     if (setOfVolToCheck.empty())
12016     {
12017       MESSAGE("No volumes found");
12018       return;
12019     }
12020
12021     // --- starting with central volumes then their neighbors, check if they are inside
12022     //     or outside the domain, until no more new neighbor volume is inside.
12023     //     Fill the group of inside volumes
12024
12025     std::map<int, double> mapOfNodeDistance2;
12026     mapOfNodeDistance2.clear();
12027     std::set<int> setOfOutsideVol;
12028     while (!setOfVolToCheck.empty())
12029     {
12030       std::set<int>::iterator it = setOfVolToCheck.begin();
12031       int vtkId = *it;
12032       MESSAGE("volume to check,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12033       bool volInside = false;
12034       vtkIdType npts = 0;
12035       vtkIdType* pts = 0;
12036       grid->GetCellPoints(vtkId, npts, pts);
12037       for (int i=0; i<npts; i++)
12038       {
12039         double distance2 = 0;
12040         if (mapOfNodeDistance2.count(pts[i]))
12041         {
12042           distance2 = mapOfNodeDistance2[pts[i]];
12043           MESSAGE("point " << pts[i] << " distance2 " << distance2);
12044         }
12045         else
12046         {
12047           double *coords = grid->GetPoint(pts[i]);
12048           gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
12049           distance2 = 1.E40;
12050           for ( size_t j = 0; j < gpnts.size(); j++ )
12051           {
12052             double d2 = aPoint.SquareDistance( gpnts[ j ]);
12053             if (d2 < distance2)
12054             {
12055               distance2 = d2;
12056               if (distance2 < radius2)
12057                 break;
12058             }
12059           }
12060           mapOfNodeDistance2[pts[i]] = distance2;
12061           MESSAGE("  point "  << pts[i]  << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " <<  coords[2]);
12062         }
12063         if (distance2 < radius2)
12064         {
12065           volInside = true; // one or more nodes inside the domain
12066           sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12067           break;
12068         }
12069       }
12070       if (volInside)
12071       {
12072         setOfInsideVol.insert(vtkId);
12073         MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12074         int neighborsVtkIds[NBMAXNEIGHBORS];
12075         int downIds[NBMAXNEIGHBORS];
12076         unsigned char downTypes[NBMAXNEIGHBORS];
12077         int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12078         for (int n = 0; n < nbNeighbors; n++)
12079           if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12080             setOfVolToCheck.insert(neighborsVtkIds[n]);
12081       }
12082       else
12083       {
12084         setOfOutsideVol.insert(vtkId);
12085         MESSAGE("  volume outside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12086       }
12087       setOfVolToCheck.erase(vtkId);
12088     }
12089   }
12090
12091   // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12092   //     If yes, add the volume to the inside set
12093
12094   bool addedInside = true;
12095   std::set<int> setOfVolToReCheck;
12096   while (addedInside)
12097   {
12098     MESSAGE(" --------------------------- re check");
12099     addedInside = false;
12100     std::set<int>::iterator itv = setOfInsideVol.begin();
12101     for (; itv != setOfInsideVol.end(); ++itv)
12102     {
12103       int vtkId = *itv;
12104       int neighborsVtkIds[NBMAXNEIGHBORS];
12105       int downIds[NBMAXNEIGHBORS];
12106       unsigned char downTypes[NBMAXNEIGHBORS];
12107       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12108       for (int n = 0; n < nbNeighbors; n++)
12109         if (!setOfInsideVol.count(neighborsVtkIds[n]))
12110           setOfVolToReCheck.insert(neighborsVtkIds[n]);
12111     }
12112     setOfVolToCheck = setOfVolToReCheck;
12113     setOfVolToReCheck.clear();
12114     while  (!setOfVolToCheck.empty())
12115     {
12116       std::set<int>::iterator it = setOfVolToCheck.begin();
12117       int vtkId = *it;
12118       if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12119       {
12120         MESSAGE("volume to recheck,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12121         int countInside = 0;
12122         int neighborsVtkIds[NBMAXNEIGHBORS];
12123         int downIds[NBMAXNEIGHBORS];
12124         unsigned char downTypes[NBMAXNEIGHBORS];
12125         int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12126         for (int n = 0; n < nbNeighbors; n++)
12127           if (setOfInsideVol.count(neighborsVtkIds[n]))
12128             countInside++;
12129         MESSAGE("countInside " << countInside);
12130         if (countInside > 1)
12131         {
12132           MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12133           setOfInsideVol.insert(vtkId);
12134           sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12135           addedInside = true;
12136         }
12137         else
12138           setOfVolToReCheck.insert(vtkId);
12139       }
12140       setOfVolToCheck.erase(vtkId);
12141     }
12142   }
12143
12144   // --- map of Downward faces at the boundary, inside the global volume
12145   //     map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12146   //     fill group of SMDS faces inside the volume (when several volume shapes)
12147   //     fill group of SMDS faces on the skin of the global volume (if skin)
12148
12149   std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12150   std::map<DownIdType, int, DownIdCompare> skinFaces;     // faces on the skin of the global volume --> corresponding cell
12151   std::set<int>::iterator it = setOfInsideVol.begin();
12152   for (; it != setOfInsideVol.end(); ++it)
12153   {
12154     int vtkId = *it;
12155     //MESSAGE("  vtkId " << vtkId  << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12156     int neighborsVtkIds[NBMAXNEIGHBORS];
12157     int downIds[NBMAXNEIGHBORS];
12158     unsigned char downTypes[NBMAXNEIGHBORS];
12159     int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12160     for (int n = 0; n < nbNeighbors; n++)
12161     {
12162       int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12163       if (neighborDim == 3)
12164       {
12165         if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12166         {
12167           DownIdType face(downIds[n], downTypes[n]);
12168           boundaryFaces[face] = vtkId;
12169         }
12170         // if the face between to volumes is in the mesh, get it (internal face between shapes)
12171         int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12172         if (vtkFaceId >= 0)
12173         {
12174           sgrpi->Add(meshDS->fromVtkToSmds(vtkFaceId));
12175           // find also the smds edges on this face
12176           int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12177           const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12178           const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12179           for (int i = 0; i < nbEdges; i++)
12180           {
12181             int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12182             if (vtkEdgeId >= 0)
12183               sgrpei->Add(meshDS->fromVtkToSmds(vtkEdgeId));
12184           }
12185         }
12186       }
12187       else if (neighborDim == 2) // skin of the volume
12188       {
12189         DownIdType face(downIds[n], downTypes[n]);
12190         skinFaces[face] = vtkId;
12191         int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12192         if (vtkFaceId >= 0)
12193           sgrps->Add(meshDS->fromVtkToSmds(vtkFaceId));
12194       }
12195     }
12196   }
12197
12198   // --- identify the edges constituting the wire of each subshape on the skin
12199   //     define polylines with the nodes of edges, equivalent to wires
12200   //     project polylines on subshapes, and partition, to get geom faces
12201
12202   std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12203   std::set<int> emptySet;
12204   emptySet.clear();
12205   std::set<int> shapeIds;
12206
12207   SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12208   while (itelem->more())
12209   {
12210     const SMDS_MeshElement *elem = itelem->next();
12211     int shapeId = elem->getshapeId();
12212     int vtkId = elem->getVtkId();
12213     if (!shapeIdToVtkIdSet.count(shapeId))
12214     {
12215       shapeIdToVtkIdSet[shapeId] = emptySet;
12216       shapeIds.insert(shapeId);
12217     }
12218     shapeIdToVtkIdSet[shapeId].insert(vtkId);
12219   }
12220
12221   std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12222   std::set<DownIdType, DownIdCompare> emptyEdges;
12223   emptyEdges.clear();
12224
12225   std::map<int, std::set<int> >::iterator itShape =  shapeIdToVtkIdSet.begin();
12226   for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12227   {
12228     int shapeId = itShape->first;
12229     MESSAGE(" --- Shape ID --- "<< shapeId);
12230     shapeIdToEdges[shapeId] = emptyEdges;
12231
12232     std::vector<int> nodesEdges;
12233
12234     std::set<int>::iterator its = itShape->second.begin();
12235     for (; its != itShape->second.end(); ++its)
12236     {
12237       int vtkId = *its;
12238       MESSAGE("     " << vtkId);
12239       int neighborsVtkIds[NBMAXNEIGHBORS];
12240       int downIds[NBMAXNEIGHBORS];
12241       unsigned char downTypes[NBMAXNEIGHBORS];
12242       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12243       for (int n = 0; n < nbNeighbors; n++)
12244       {
12245         if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12246           continue;
12247         int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
12248         const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12249         if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12250         {
12251           DownIdType edge(downIds[n], downTypes[n]);
12252           if (!shapeIdToEdges[shapeId].count(edge))
12253           {
12254             shapeIdToEdges[shapeId].insert(edge);
12255             int vtkNodeId[3];
12256             int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12257             nodesEdges.push_back(vtkNodeId[0]);
12258             nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12259             MESSAGE("       --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12260           }
12261         }
12262       }
12263     }
12264
12265     std::list<int> order;
12266     order.clear();
12267     if (nodesEdges.size() > 0)
12268     {
12269       order.push_back(nodesEdges[0]); MESSAGE("       --- back " << order.back()+1); // SMDS id = VTK id + 1;
12270       nodesEdges[0] = -1;
12271       order.push_back(nodesEdges[1]); MESSAGE("       --- back " << order.back()+1);
12272       nodesEdges[1] = -1; // do not reuse this edge
12273       bool found = true;
12274       while (found)
12275       {
12276         int nodeTofind = order.back(); // try first to push back
12277         int i = 0;
12278         for ( i = 0; i < (int)nodesEdges.size(); i++ )
12279           if (nodesEdges[i] == nodeTofind)
12280             break;
12281         if ( i == (int) nodesEdges.size() )
12282           found = false; // no follower found on back
12283         else
12284         {
12285           if (i%2) // odd ==> use the previous one
12286             if (nodesEdges[i-1] < 0)
12287               found = false;
12288             else
12289             {
12290               order.push_back(nodesEdges[i-1]); MESSAGE("       --- back " << order.back()+1);
12291               nodesEdges[i-1] = -1;
12292             }
12293           else // even ==> use the next one
12294             if (nodesEdges[i+1] < 0)
12295               found = false;
12296             else
12297             {
12298               order.push_back(nodesEdges[i+1]); MESSAGE("       --- back " << order.back()+1);
12299               nodesEdges[i+1] = -1;
12300             }
12301         }
12302         if (found)
12303           continue;
12304         // try to push front
12305         found = true;
12306         nodeTofind = order.front(); // try to push front
12307         for ( i = 0;  i < (int)nodesEdges.size(); i++ )
12308           if ( nodesEdges[i] == nodeTofind )
12309             break;
12310         if ( i == (int)nodesEdges.size() )
12311         {
12312           found = false; // no predecessor found on front
12313           continue;
12314         }
12315         if (i%2) // odd ==> use the previous one
12316           if (nodesEdges[i-1] < 0)
12317             found = false;
12318           else
12319           {
12320             order.push_front(nodesEdges[i-1]); MESSAGE("       --- front " << order.front()+1);
12321             nodesEdges[i-1] = -1;
12322           }
12323         else // even ==> use the next one
12324           if (nodesEdges[i+1] < 0)
12325             found = false;
12326           else
12327           {
12328             order.push_front(nodesEdges[i+1]); MESSAGE("       --- front " << order.front()+1);
12329             nodesEdges[i+1] = -1;
12330           }
12331       }
12332     }
12333
12334
12335     std::vector<int> nodes;
12336     nodes.push_back(shapeId);
12337     std::list<int>::iterator itl = order.begin();
12338     for (; itl != order.end(); itl++)
12339     {
12340       nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12341       MESSAGE("              ordered node " << nodes[nodes.size()-1]);
12342     }
12343     listOfListOfNodes.push_back(nodes);
12344   }
12345
12346   //     partition geom faces with blocFissure
12347   //     mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12348   //     mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12349
12350   return;
12351 }
12352
12353
12354 //================================================================================
12355 /*!
12356  * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12357  * The created 2D mesh elements based on nodes of free faces of boundary volumes
12358  * \return TRUE if operation has been completed successfully, FALSE otherwise
12359  */
12360 //================================================================================
12361
12362 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12363 {
12364   // iterates on volume elements and detect all free faces on them
12365   SMESHDS_Mesh* aMesh = GetMeshDS();
12366   if (!aMesh)
12367     return false;
12368
12369   ElemFeatures faceType( SMDSAbs_Face );
12370   int nbFree = 0, nbExisted = 0, nbCreated = 0;
12371   SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12372   while(vIt->more())
12373   {
12374     const SMDS_MeshVolume* volume = vIt->next();
12375     SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12376     vTool.SetExternalNormal();
12377     const int iQuad = volume->IsQuadratic();
12378     faceType.SetQuad( iQuad );
12379     for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12380     {
12381       if (!vTool.IsFreeFace(iface))
12382         continue;
12383       nbFree++;
12384       vector<const SMDS_MeshNode *> nodes;
12385       int nbFaceNodes = vTool.NbFaceNodes(iface);
12386       const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12387       int inode = 0;
12388       for ( ; inode < nbFaceNodes; inode += iQuad+1)
12389         nodes.push_back(faceNodes[inode]);
12390
12391       if (iQuad) // add medium nodes
12392       {
12393         for ( inode = 1; inode < nbFaceNodes; inode += 2)
12394           nodes.push_back(faceNodes[inode]);
12395         if ( nbFaceNodes == 9 ) // bi-quadratic quad
12396           nodes.push_back(faceNodes[8]);
12397       }
12398       // add new face based on volume nodes
12399       if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) )
12400       {
12401         nbExisted++; // face already exsist
12402       }
12403       else
12404       {
12405         AddElement( nodes, faceType.SetPoly( nbFaceNodes/(iQuad+1) > 4 ));
12406         nbCreated++;
12407       }
12408     }
12409   }
12410   return ( nbFree == ( nbExisted + nbCreated ));
12411 }
12412
12413 namespace
12414 {
12415   inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12416   {
12417     if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12418       return n;
12419     return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12420   }
12421 }
12422 //================================================================================
12423 /*!
12424  * \brief Creates missing boundary elements
12425  *  \param elements - elements whose boundary is to be checked
12426  *  \param dimension - defines type of boundary elements to create
12427  *  \param group - a group to store created boundary elements in
12428  *  \param targetMesh - a mesh to store created boundary elements in
12429  *  \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12430  *  \param toCopyExistingBoundary - if true, not only new but also pre-existing
12431  *                                boundary elements will be copied into the targetMesh
12432  *  \param toAddExistingBondary - if true, not only new but also pre-existing
12433  *                                boundary elements will be added into the new group
12434  *  \param aroundElements - if true, elements will be created on boundary of given
12435  *                          elements else, on boundary of the whole mesh.
12436  * \return nb of added boundary elements
12437  */
12438 //================================================================================
12439
12440 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12441                                        Bnd_Dimension           dimension,
12442                                        SMESH_Group*            group/*=0*/,
12443                                        SMESH_Mesh*             targetMesh/*=0*/,
12444                                        bool                    toCopyElements/*=false*/,
12445                                        bool                    toCopyExistingBoundary/*=false*/,
12446                                        bool                    toAddExistingBondary/*= false*/,
12447                                        bool                    aroundElements/*= false*/)
12448 {
12449   SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12450   SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12451   // hope that all elements are of the same type, do not check them all
12452   if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12453     throw SALOME_Exception(LOCALIZED("wrong element type"));
12454
12455   if ( !targetMesh )
12456     toCopyElements = toCopyExistingBoundary = false;
12457
12458   SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12459   SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12460   int nbAddedBnd = 0;
12461
12462   // editor adding present bnd elements and optionally holding elements to add to the group
12463   SMESH_MeshEditor* presentEditor;
12464   SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12465   presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12466
12467   SMESH_MesherHelper helper( *myMesh );
12468   const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12469   SMDS_VolumeTool vTool;
12470   TIDSortedElemSet avoidSet;
12471   const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12472   size_t inode;
12473
12474   typedef vector<const SMDS_MeshNode*> TConnectivity;
12475   TConnectivity tgtNodes;
12476   ElemFeatures elemKind( missType ), elemToCopy;
12477
12478   vector<const SMDS_MeshElement*> presentBndElems;
12479   vector<TConnectivity>           missingBndElems;
12480   vector<int>                     freeFacets;
12481   TConnectivity nodes, elemNodes;
12482
12483   SMDS_ElemIteratorPtr eIt;
12484   if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12485   else                  eIt = elemSetIterator( elements );
12486
12487   while (eIt->more())
12488   {
12489     const SMDS_MeshElement* elem = eIt->next();
12490     const int              iQuad = elem->IsQuadratic();
12491     elemKind.SetQuad( iQuad );
12492
12493     // ------------------------------------------------------------------------------------
12494     // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12495     // ------------------------------------------------------------------------------------
12496     presentBndElems.clear();
12497     missingBndElems.clear();
12498     freeFacets.clear(); nodes.clear(); elemNodes.clear();
12499     if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12500     {
12501       const SMDS_MeshElement* otherVol = 0;
12502       for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12503       {
12504         if ( !vTool.IsFreeFace(iface, &otherVol) &&
12505              ( !aroundElements || elements.count( otherVol )))
12506           continue;
12507         freeFacets.push_back( iface );
12508       }
12509       if ( missType == SMDSAbs_Face )
12510         vTool.SetExternalNormal();
12511       for ( size_t i = 0; i < freeFacets.size(); ++i )
12512       {
12513         int                iface = freeFacets[i];
12514         const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12515         const size_t nbFaceNodes = vTool.NbFaceNodes (iface);
12516         if ( missType == SMDSAbs_Edge ) // boundary edges
12517         {
12518           nodes.resize( 2+iQuad );
12519           for ( size_t i = 0; i < nbFaceNodes; i += 1+iQuad )
12520           {
12521             for ( size_t j = 0; j < nodes.size(); ++j )
12522               nodes[ j ] = nn[ i+j ];
12523             if ( const SMDS_MeshElement* edge =
12524                  aMesh->FindElement( nodes, SMDSAbs_Edge, /*noMedium=*/false ))
12525               presentBndElems.push_back( edge );
12526             else
12527               missingBndElems.push_back( nodes );
12528           }
12529         }
12530         else // boundary face
12531         {
12532           nodes.clear();
12533           for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12534             nodes.push_back( nn[inode] ); // add corner nodes
12535           if (iQuad)
12536             for ( inode = 1; inode < nbFaceNodes; inode += 2)
12537               nodes.push_back( nn[inode] ); // add medium nodes
12538           int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12539           if ( iCenter > 0 )
12540             nodes.push_back( vTool.GetNodes()[ iCenter ] );
12541
12542           if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12543                                                                SMDSAbs_Face, /*noMedium=*/false ))
12544             presentBndElems.push_back( f );
12545           else
12546             missingBndElems.push_back( nodes );
12547
12548           if ( targetMesh != myMesh )
12549           {
12550             // add 1D elements on face boundary to be added to a new mesh
12551             const SMDS_MeshElement* edge;
12552             for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12553             {
12554               if ( iQuad )
12555                 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12556               else
12557                 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12558               if ( edge && avoidSet.insert( edge ).second )
12559                 presentBndElems.push_back( edge );
12560             }
12561           }
12562         }
12563       }
12564     }
12565     else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12566     {
12567       avoidSet.clear(), avoidSet.insert( elem );
12568       elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesElemIterator() ),
12569                         SMDS_MeshElement::iterator() );
12570       elemNodes.push_back( elemNodes[0] );
12571       nodes.resize( 2 + iQuad );
12572       const int nbLinks = elem->NbCornerNodes();
12573       for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12574       {
12575         nodes[0] = elemNodes[iN];
12576         nodes[1] = elemNodes[iN+1+iQuad];
12577         if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12578           continue; // not free link
12579
12580         if ( iQuad ) nodes[2] = elemNodes[iN+1];
12581         if ( const SMDS_MeshElement* edge =
12582              aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12583           presentBndElems.push_back( edge );
12584         else
12585           missingBndElems.push_back( nodes );
12586       }
12587     }
12588
12589     // ---------------------------------
12590     // 2. Add missing boundary elements
12591     // ---------------------------------
12592     if ( targetMesh != myMesh )
12593       // instead of making a map of nodes in this mesh and targetMesh,
12594       // we create nodes with same IDs.
12595       for ( size_t i = 0; i < missingBndElems.size(); ++i )
12596       {
12597         TConnectivity& srcNodes = missingBndElems[i];
12598         tgtNodes.resize( srcNodes.size() );
12599         for ( inode = 0; inode < srcNodes.size(); ++inode )
12600           tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12601         if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( tgtNodes,
12602                                                                    missType,
12603                                                                    /*noMedium=*/false))
12604           continue;
12605         tgtEditor.AddElement( tgtNodes, elemKind.SetPoly( tgtNodes.size()/(iQuad+1) > 4 ));
12606         ++nbAddedBnd;
12607       }
12608     else
12609       for ( size_t i = 0; i < missingBndElems.size(); ++i )
12610       {
12611         TConnectivity& nodes = missingBndElems[ i ];
12612         if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12613                                                                    missType,
12614                                                                    /*noMedium=*/false))
12615           continue;
12616         SMDS_MeshElement* newElem =
12617           tgtEditor.AddElement( nodes, elemKind.SetPoly( nodes.size()/(iQuad+1) > 4 ));
12618         nbAddedBnd += bool( newElem );
12619
12620         // try to set a new element to a shape
12621         if ( myMesh->HasShapeToMesh() )
12622         {
12623           bool ok = true;
12624           set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12625           const size_t nbN = nodes.size() / (iQuad+1 );
12626           for ( inode = 0; inode < nbN && ok; ++inode )
12627           {
12628             pair<int, TopAbs_ShapeEnum> i_stype =
12629               helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12630             if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12631               mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12632           }
12633           if ( ok && mediumShapes.size() > 1 )
12634           {
12635             set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12636             pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12637             for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12638             {
12639               if (( ok = ( stype_i->first != stype_i_0.first )))
12640                 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12641                                         aMesh->IndexToShape( stype_i_0.second ));
12642             }
12643           }
12644           if ( ok && mediumShapes.begin()->first == missShapeType )
12645             aMesh->SetMeshElementOnShape( newElem, mediumShapes.begin()->second );
12646         }
12647       }
12648
12649     // ----------------------------------
12650     // 3. Copy present boundary elements
12651     // ----------------------------------
12652     if ( toCopyExistingBoundary )
12653       for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12654       {
12655         const SMDS_MeshElement* e = presentBndElems[i];
12656         tgtNodes.resize( e->NbNodes() );
12657         for ( inode = 0; inode < tgtNodes.size(); ++inode )
12658           tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12659         presentEditor->AddElement( tgtNodes, elemToCopy.Init( e ));
12660       }
12661     else // store present elements to add them to a group
12662       for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12663       {
12664         presentEditor->myLastCreatedElems.Append( presentBndElems[ i ]);
12665       }
12666
12667   } // loop on given elements
12668
12669   // ---------------------------------------------
12670   // 4. Fill group with boundary elements
12671   // ---------------------------------------------
12672   if ( group )
12673   {
12674     if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12675       for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i )
12676         g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 ));
12677   }
12678   tgtEditor.myLastCreatedElems.Clear();
12679   tgtEditor2.myLastCreatedElems.Clear();
12680
12681   // -----------------------
12682   // 5. Copy given elements
12683   // -----------------------
12684   if ( toCopyElements && targetMesh != myMesh )
12685   {
12686     if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12687     else                  eIt = elemSetIterator( elements );
12688     while (eIt->more())
12689     {
12690       const SMDS_MeshElement* elem = eIt->next();
12691       tgtNodes.resize( elem->NbNodes() );
12692       for ( inode = 0; inode < tgtNodes.size(); ++inode )
12693         tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12694       tgtEditor.AddElement( tgtNodes, elemToCopy.Init( elem ));
12695
12696       tgtEditor.myLastCreatedElems.Clear();
12697     }
12698   }
12699   return nbAddedBnd;
12700 }
12701
12702 //================================================================================
12703 /*!
12704  * \brief Copy node position and set \a to node on the same geometry
12705  */
12706 //================================================================================
12707
12708 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
12709                                      const SMDS_MeshNode* to )
12710 {
12711   if ( !from || !to ) return;
12712
12713   SMDS_PositionPtr pos = from->GetPosition();
12714   if ( !pos || from->getshapeId() < 1 ) return;
12715
12716   switch ( pos->GetTypeOfPosition() )
12717   {
12718   case SMDS_TOP_3DSPACE: break;
12719
12720   case SMDS_TOP_FACE:
12721   {
12722     const SMDS_FacePosition* fPos = static_cast< const SMDS_FacePosition* >( pos );
12723     GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
12724                                 fPos->GetUParameter(), fPos->GetVParameter() );
12725     break;
12726   }
12727   case SMDS_TOP_EDGE:
12728   {
12729     // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
12730     const SMDS_EdgePosition* ePos = static_cast< const SMDS_EdgePosition* >( pos );
12731     GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
12732     break;
12733   }
12734   case SMDS_TOP_VERTEX:
12735   {
12736     GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
12737     break;
12738   }
12739   case SMDS_TOP_UNSPEC:
12740   default:;
12741   }
12742 }