Salome HOME
23305: [EDF 10301] Extrusion with scaling
[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 except those
478  *        nodes on which a 0D element already exists.
479  *  \param elements - Elements on whose nodes to create 0D elements; if empty, 
480  *                    the all mesh is treated
481  *  \param all0DElems - returns all 0D elements found or created on nodes of \a elements
482  */
483 //================================================================================
484
485 void SMESH_MeshEditor::Create0DElementsOnAllNodes( const TIDSortedElemSet& elements,
486                                                    TIDSortedElemSet&       all0DElems )
487 {
488   SMDS_ElemIteratorPtr elemIt;
489   vector< const SMDS_MeshElement* > allNodes;
490   if ( elements.empty() )
491   {
492     allNodes.reserve( GetMeshDS()->NbNodes() );
493     elemIt = GetMeshDS()->elementsIterator( SMDSAbs_Node );
494     while ( elemIt->more() )
495       allNodes.push_back( elemIt->next() );
496
497     elemIt = elemSetIterator( allNodes );
498   }
499   else
500   {
501     elemIt = elemSetIterator( elements );
502   }
503
504   while ( elemIt->more() )
505   {
506     const SMDS_MeshElement* e = elemIt->next();
507     SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
508     while ( nodeIt->more() )
509     {
510       const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
511       SMDS_ElemIteratorPtr it0D = n->GetInverseElementIterator( SMDSAbs_0DElement );
512       if ( it0D->more() )
513         all0DElems.insert( it0D->next() );
514       else {
515         myLastCreatedElems.Append( GetMeshDS()->Add0DElement( n ));
516         all0DElems.insert( myLastCreatedElems.Last() );
517       }
518     }
519   }
520 }
521
522 //=======================================================================
523 //function : FindShape
524 //purpose  : Return an index of the shape theElem is on
525 //           or zero if a shape not found
526 //=======================================================================
527
528 int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem)
529 {
530   myLastCreatedElems.Clear();
531   myLastCreatedNodes.Clear();
532
533   SMESHDS_Mesh * aMesh = GetMeshDS();
534   if ( aMesh->ShapeToMesh().IsNull() )
535     return 0;
536
537   int aShapeID = theElem->getshapeId();
538   if ( aShapeID < 1 )
539     return 0;
540
541   if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ))
542     if ( sm->Contains( theElem ))
543       return aShapeID;
544
545   if ( theElem->GetType() == SMDSAbs_Node ) {
546     MESSAGE( ":( Error: invalid myShapeId of node " << theElem->GetID() );
547   }
548   else {
549     MESSAGE( ":( Error: invalid myShapeId of element " << theElem->GetID() );
550   }
551
552   TopoDS_Shape aShape; // the shape a node of theElem is on
553   if ( theElem->GetType() != SMDSAbs_Node )
554   {
555     SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
556     while ( nodeIt->more() ) {
557       const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
558       if ((aShapeID = node->getshapeId()) > 0) {
559         if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ) ) {
560           if ( sm->Contains( theElem ))
561             return aShapeID;
562           if ( aShape.IsNull() )
563             aShape = aMesh->IndexToShape( aShapeID );
564         }
565       }
566     }
567   }
568
569   // None of nodes is on a proper shape,
570   // find the shape among ancestors of aShape on which a node is
571   if ( !aShape.IsNull() ) {
572     TopTools_ListIteratorOfListOfShape ancIt( GetMesh()->GetAncestors( aShape ));
573     for ( ; ancIt.More(); ancIt.Next() ) {
574       SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() );
575       if ( sm && sm->Contains( theElem ))
576         return aMesh->ShapeToIndex( ancIt.Value() );
577     }
578   }
579   else
580   {
581     SMESHDS_SubMeshIteratorPtr smIt = GetMeshDS()->SubMeshes();
582     while ( const SMESHDS_SubMesh* sm = smIt->next() )
583       if ( sm->Contains( theElem ))
584         return sm->GetID();
585   }
586
587   return 0;
588 }
589
590 //=======================================================================
591 //function : IsMedium
592 //purpose  :
593 //=======================================================================
594
595 bool SMESH_MeshEditor::IsMedium(const SMDS_MeshNode*      node,
596                                 const SMDSAbs_ElementType typeToCheck)
597 {
598   bool isMedium = false;
599   SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(typeToCheck);
600   while (it->more() && !isMedium ) {
601     const SMDS_MeshElement* elem = it->next();
602     isMedium = elem->IsMediumNode(node);
603   }
604   return isMedium;
605 }
606
607 //=======================================================================
608 //function : shiftNodesQuadTria
609 //purpose  : Shift nodes in the array corresponded to quadratic triangle
610 //           example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
611 //=======================================================================
612
613 static void shiftNodesQuadTria(vector< const SMDS_MeshNode* >& aNodes)
614 {
615   const SMDS_MeshNode* nd1 = aNodes[0];
616   aNodes[0] = aNodes[1];
617   aNodes[1] = aNodes[2];
618   aNodes[2] = nd1;
619   const SMDS_MeshNode* nd2 = aNodes[3];
620   aNodes[3] = aNodes[4];
621   aNodes[4] = aNodes[5];
622   aNodes[5] = nd2;
623 }
624
625 //=======================================================================
626 //function : nbEdgeConnectivity
627 //purpose  : return number of the edges connected with the theNode.
628 //           if theEdges has connections with the other type of the
629 //           elements, return -1
630 //=======================================================================
631
632 static int nbEdgeConnectivity(const SMDS_MeshNode* theNode)
633 {
634   // SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator();
635   // int nb=0;
636   // while(elemIt->more()) {
637   //   elemIt->next();
638   //   nb++;
639   // }
640   // return nb;
641   return theNode->NbInverseElements();
642 }
643
644 //=======================================================================
645 //function : getNodesFromTwoTria
646 //purpose  : 
647 //=======================================================================
648
649 static bool getNodesFromTwoTria(const SMDS_MeshElement * theTria1,
650                                 const SMDS_MeshElement * theTria2,
651                                 vector< const SMDS_MeshNode*>& N1,
652                                 vector< const SMDS_MeshNode*>& N2)
653 {
654   N1.assign( theTria1->begin_nodes(), theTria1->end_nodes() );
655   if ( N1.size() < 6 ) return false;
656   N2.assign( theTria2->begin_nodes(), theTria2->end_nodes() );
657   if ( N2.size() < 6 ) return false;
658
659   int sames[3] = {-1,-1,-1};
660   int nbsames = 0;
661   int i, j;
662   for(i=0; i<3; i++) {
663     for(j=0; j<3; j++) {
664       if(N1[i]==N2[j]) {
665         sames[i] = j;
666         nbsames++;
667         break;
668       }
669     }
670   }
671   if(nbsames!=2) return false;
672   if(sames[0]>-1) {
673     shiftNodesQuadTria(N1);
674     if(sames[1]>-1) {
675       shiftNodesQuadTria(N1);
676     }
677   }
678   i = sames[0] + sames[1] + sames[2];
679   for(; i<2; i++) {
680     shiftNodesQuadTria(N2);
681   }
682   // now we receive following N1 and N2 (using numeration as in the image below)
683   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
684   // i.e. first nodes from both arrays form a new diagonal
685   return true;
686 }
687
688 //=======================================================================
689 //function : InverseDiag
690 //purpose  : Replace two neighbour triangles with ones built on the same 4 nodes
691 //           but having other common link.
692 //           Return False if args are improper
693 //=======================================================================
694
695 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1,
696                                     const SMDS_MeshElement * theTria2 )
697 {
698   myLastCreatedElems.Clear();
699   myLastCreatedNodes.Clear();
700
701   if (!theTria1 || !theTria2)
702     return false;
703
704   const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( theTria1 );
705   if (!F1) return false;
706   const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( theTria2 );
707   if (!F2) return false;
708   if ((theTria1->GetEntityType() == SMDSEntity_Triangle) &&
709       (theTria2->GetEntityType() == SMDSEntity_Triangle)) {
710
711     //  1 +--+ A  theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
712     //    | /|    theTria2: ( B A 2 ) B->1 ( 1 A 2 )   |\ |
713     //    |/ |                                         | \|
714     //  B +--+ 2                                     B +--+ 2
715
716     // put nodes in array and find out indices of the same ones
717     const SMDS_MeshNode* aNodes [6];
718     int sameInd [] = { -1, -1, -1, -1, -1, -1 };
719     int i = 0;
720     SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
721     while ( it->more() ) {
722       aNodes[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
723
724       if ( i > 2 ) // theTria2
725         // find same node of theTria1
726         for ( int j = 0; j < 3; j++ )
727           if ( aNodes[ i ] == aNodes[ j ]) {
728             sameInd[ j ] = i;
729             sameInd[ i ] = j;
730             break;
731           }
732       // next
733       i++;
734       if ( i == 3 ) {
735         if ( it->more() )
736           return false; // theTria1 is not a triangle
737         it = theTria2->nodesIterator();
738       }
739       if ( i == 6 && it->more() )
740         return false; // theTria2 is not a triangle
741     }
742
743     // find indices of 1,2 and of A,B in theTria1
744     int iA = -1, iB = 0, i1 = 0, i2 = 0;
745     for ( i = 0; i < 6; i++ ) {
746       if ( sameInd [ i ] == -1 ) {
747         if ( i < 3 ) i1 = i;
748         else         i2 = i;
749       }
750       else if (i < 3) {
751         if ( iA >= 0) iB = i;
752         else          iA = i;
753       }
754     }
755     // nodes 1 and 2 should not be the same
756     if ( aNodes[ i1 ] == aNodes[ i2 ] )
757       return false;
758
759     // theTria1: A->2
760     aNodes[ iA ] = aNodes[ i2 ];
761     // theTria2: B->1
762     aNodes[ sameInd[ iB ]] = aNodes[ i1 ];
763
764     GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 );
765     GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 );
766
767     return true;
768
769   } // end if(F1 && F2)
770
771   // check case of quadratic faces
772   if (theTria1->GetEntityType() != SMDSEntity_Quad_Triangle &&
773       theTria1->GetEntityType() != SMDSEntity_BiQuad_Triangle)
774     return false;
775   if (theTria2->GetEntityType() != SMDSEntity_Quad_Triangle&&
776       theTria2->GetEntityType() != SMDSEntity_BiQuad_Triangle)
777     return false;
778
779   //       5
780   //  1 +--+--+ 2  theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
781   //    |    /|    theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
782   //    |   / |
783   //  7 +  +  + 6
784   //    | /9  |
785   //    |/    |
786   //  4 +--+--+ 3
787   //       8
788
789   vector< const SMDS_MeshNode* > N1;
790   vector< const SMDS_MeshNode* > N2;
791   if(!getNodesFromTwoTria(theTria1,theTria2,N1,N2))
792     return false;
793   // now we receive following N1 and N2 (using numeration as above image)
794   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
795   // i.e. first nodes from both arrays determ new diagonal
796
797   vector< const SMDS_MeshNode*> N1new( N1.size() );
798   vector< const SMDS_MeshNode*> N2new( N2.size() );
799   N1new.back() = N1.back(); // central node of biquadratic
800   N2new.back() = N2.back();
801   N1new[0] = N1[0];  N2new[0] = N1[0];
802   N1new[1] = N2[0];  N2new[1] = N1[1];
803   N1new[2] = N2[1];  N2new[2] = N2[0];
804   N1new[3] = N1[4];  N2new[3] = N1[3];
805   N1new[4] = N2[3];  N2new[4] = N2[5];
806   N1new[5] = N1[5];  N2new[5] = N1[4];
807   // change nodes in faces
808   GetMeshDS()->ChangeElementNodes( theTria1, &N1new[0], N1new.size() );
809   GetMeshDS()->ChangeElementNodes( theTria2, &N2new[0], N2new.size() );
810
811   // move the central node of biquadratic triangle
812   SMESH_MesherHelper helper( *GetMesh() );
813   for ( int is2nd = 0; is2nd < 2; ++is2nd )
814   {
815     const SMDS_MeshElement*         tria = is2nd ? theTria2 : theTria1;
816     vector< const SMDS_MeshNode*>& nodes = is2nd ? N2new : N1new;
817     if ( nodes.size() < 7 )
818       continue;
819     helper.SetSubShape( tria->getshapeId() );
820     const TopoDS_Face& F = TopoDS::Face( helper.GetSubShape() );
821     gp_Pnt xyz;
822     if ( F.IsNull() )
823     {
824       xyz = ( SMESH_TNodeXYZ( nodes[3] ) +
825               SMESH_TNodeXYZ( nodes[4] ) +
826               SMESH_TNodeXYZ( nodes[5] )) / 3.;
827     }
828     else
829     {
830       bool checkUV;
831       gp_XY uv = ( helper.GetNodeUV( F, nodes[3], nodes[2], &checkUV ) +
832                    helper.GetNodeUV( F, nodes[4], nodes[0], &checkUV ) +
833                    helper.GetNodeUV( F, nodes[5], nodes[1], &checkUV )) / 3.;
834       TopLoc_Location loc;
835       Handle(Geom_Surface) S = BRep_Tool::Surface(F,loc);
836       xyz = S->Value( uv.X(), uv.Y() );
837       xyz.Transform( loc );
838       if ( nodes[6]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE &&  // set UV
839            nodes[6]->getshapeId() > 0 )
840         GetMeshDS()->SetNodeOnFace( nodes[6], nodes[6]->getshapeId(), uv.X(), uv.Y() );
841     }
842     GetMeshDS()->MoveNode( nodes[6], xyz.X(), xyz.Y(), xyz.Z() );
843   }
844   return true;
845 }
846
847 //=======================================================================
848 //function : findTriangles
849 //purpose  : find triangles sharing theNode1-theNode2 link
850 //=======================================================================
851
852 static bool findTriangles(const SMDS_MeshNode *    theNode1,
853                           const SMDS_MeshNode *    theNode2,
854                           const SMDS_MeshElement*& theTria1,
855                           const SMDS_MeshElement*& theTria2)
856 {
857   if ( !theNode1 || !theNode2 ) return false;
858
859   theTria1 = theTria2 = 0;
860
861   set< const SMDS_MeshElement* > emap;
862   SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face);
863   while (it->more()) {
864     const SMDS_MeshElement* elem = it->next();
865     if ( elem->NbCornerNodes() == 3 )
866       emap.insert( elem );
867   }
868   it = theNode2->GetInverseElementIterator(SMDSAbs_Face);
869   while (it->more()) {
870     const SMDS_MeshElement* elem = it->next();
871     if ( emap.count( elem )) {
872       if ( !theTria1 )
873       {
874         theTria1 = elem;
875       }
876       else  
877       {
878         theTria2 = elem;
879         // theTria1 must be element with minimum ID
880         if ( theTria2->GetID() < theTria1->GetID() )
881           std::swap( theTria2, theTria1 );
882         return true;
883       }
884     }
885   }
886   return false;
887 }
888
889 //=======================================================================
890 //function : InverseDiag
891 //purpose  : Replace two neighbour triangles sharing theNode1-theNode2 link
892 //           with ones built on the same 4 nodes but having other common link.
893 //           Return false if proper faces not found
894 //=======================================================================
895
896 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
897                                     const SMDS_MeshNode * theNode2)
898 {
899   myLastCreatedElems.Clear();
900   myLastCreatedNodes.Clear();
901
902   const SMDS_MeshElement *tr1, *tr2;
903   if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
904     return false;
905
906   const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
907   if (!F1) return false;
908   const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
909   if (!F2) return false;
910   if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
911       (tr2->GetEntityType() == SMDSEntity_Triangle)) {
912
913     //  1 +--+ A  tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
914     //    | /|    tr2: ( B A 2 ) B->1 ( 1 A 2 )   |\ |
915     //    |/ |                                    | \|
916     //  B +--+ 2                                B +--+ 2
917
918     // put nodes in array
919     // and find indices of 1,2 and of A in tr1 and of B in tr2
920     int i, iA1 = 0, i1 = 0;
921     const SMDS_MeshNode* aNodes1 [3];
922     SMDS_ElemIteratorPtr it;
923     for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) {
924       aNodes1[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
925       if ( aNodes1[ i ] == theNode1 )
926         iA1 = i; // node A in tr1
927       else if ( aNodes1[ i ] != theNode2 )
928         i1 = i;  // node 1
929     }
930     int iB2 = 0, i2 = 0;
931     const SMDS_MeshNode* aNodes2 [3];
932     for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) {
933       aNodes2[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
934       if ( aNodes2[ i ] == theNode2 )
935         iB2 = i; // node B in tr2
936       else if ( aNodes2[ i ] != theNode1 )
937         i2 = i;  // node 2
938     }
939
940     // nodes 1 and 2 should not be the same
941     if ( aNodes1[ i1 ] == aNodes2[ i2 ] )
942       return false;
943
944     // tr1: A->2
945     aNodes1[ iA1 ] = aNodes2[ i2 ];
946     // tr2: B->1
947     aNodes2[ iB2 ] = aNodes1[ i1 ];
948
949     GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 );
950     GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 );
951
952     return true;
953   }
954
955   // check case of quadratic faces
956   return InverseDiag(tr1,tr2);
957 }
958
959 //=======================================================================
960 //function : getQuadrangleNodes
961 //purpose  : fill theQuadNodes - nodes of a quadrangle resulting from
962 //           fusion of triangles tr1 and tr2 having shared link on
963 //           theNode1 and theNode2
964 //=======================================================================
965
966 bool getQuadrangleNodes(const SMDS_MeshNode *    theQuadNodes [],
967                         const SMDS_MeshNode *    theNode1,
968                         const SMDS_MeshNode *    theNode2,
969                         const SMDS_MeshElement * tr1,
970                         const SMDS_MeshElement * tr2 )
971 {
972   if( tr1->NbNodes() != tr2->NbNodes() )
973     return false;
974   // find the 4-th node to insert into tr1
975   const SMDS_MeshNode* n4 = 0;
976   SMDS_ElemIteratorPtr it = tr2->nodesIterator();
977   int i=0;
978   while ( !n4 && i<3 ) {
979     const SMDS_MeshNode * n = cast2Node( it->next() );
980     i++;
981     bool isDiag = ( n == theNode1 || n == theNode2 );
982     if ( !isDiag )
983       n4 = n;
984   }
985   // Make an array of nodes to be in a quadrangle
986   int iNode = 0, iFirstDiag = -1;
987   it = tr1->nodesIterator();
988   i=0;
989   while ( i<3 ) {
990     const SMDS_MeshNode * n = cast2Node( it->next() );
991     i++;
992     bool isDiag = ( n == theNode1 || n == theNode2 );
993     if ( isDiag ) {
994       if ( iFirstDiag < 0 )
995         iFirstDiag = iNode;
996       else if ( iNode - iFirstDiag == 1 )
997         theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes
998     }
999     else if ( n == n4 ) {
1000       return false; // tr1 and tr2 should not have all the same nodes
1001     }
1002     theQuadNodes[ iNode++ ] = n;
1003   }
1004   if ( iNode == 3 ) // diagonal nodes have 0 and 2 indices
1005     theQuadNodes[ iNode ] = n4;
1006
1007   return true;
1008 }
1009
1010 //=======================================================================
1011 //function : DeleteDiag
1012 //purpose  : Replace two neighbour triangles sharing theNode1-theNode2 link
1013 //           with a quadrangle built on the same 4 nodes.
1014 //           Return false if proper faces not found
1015 //=======================================================================
1016
1017 bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
1018                                    const SMDS_MeshNode * theNode2)
1019 {
1020   myLastCreatedElems.Clear();
1021   myLastCreatedNodes.Clear();
1022
1023   const SMDS_MeshElement *tr1, *tr2;
1024   if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
1025     return false;
1026
1027   const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
1028   if (!F1) return false;
1029   const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
1030   if (!F2) return false;
1031   SMESHDS_Mesh * aMesh = GetMeshDS();
1032
1033   if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
1034       (tr2->GetEntityType() == SMDSEntity_Triangle)) {
1035
1036     const SMDS_MeshNode* aNodes [ 4 ];
1037     if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 ))
1038       return false;
1039
1040     const SMDS_MeshElement* newElem = 0;
1041     newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3] );
1042     myLastCreatedElems.Append(newElem);
1043     AddToSameGroups( newElem, tr1, aMesh );
1044     int aShapeId = tr1->getshapeId();
1045     if ( aShapeId )
1046       {
1047         aMesh->SetMeshElementOnShape( newElem, aShapeId );
1048       }
1049     aMesh->RemoveElement( tr1 );
1050     aMesh->RemoveElement( tr2 );
1051
1052     return true;
1053   }
1054
1055   // check case of quadratic faces
1056   if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
1057     return false;
1058   if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
1059     return false;
1060
1061   //       5
1062   //  1 +--+--+ 2  tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
1063   //    |    /|    tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
1064   //    |   / |
1065   //  7 +  +  + 6
1066   //    | /9  |
1067   //    |/    |
1068   //  4 +--+--+ 3
1069   //       8
1070
1071   vector< const SMDS_MeshNode* > N1;
1072   vector< const SMDS_MeshNode* > N2;
1073   if(!getNodesFromTwoTria(tr1,tr2,N1,N2))
1074     return false;
1075   // now we receive following N1 and N2 (using numeration as above image)
1076   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
1077   // i.e. first nodes from both arrays determ new diagonal
1078
1079   const SMDS_MeshNode* aNodes[8];
1080   aNodes[0] = N1[0];
1081   aNodes[1] = N1[1];
1082   aNodes[2] = N2[0];
1083   aNodes[3] = N2[1];
1084   aNodes[4] = N1[3];
1085   aNodes[5] = N2[5];
1086   aNodes[6] = N2[3];
1087   aNodes[7] = N1[5];
1088
1089   const SMDS_MeshElement* newElem = 0;
1090   newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
1091                             aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
1092   myLastCreatedElems.Append(newElem);
1093   AddToSameGroups( newElem, tr1, aMesh );
1094   int aShapeId = tr1->getshapeId();
1095   if ( aShapeId )
1096     {
1097       aMesh->SetMeshElementOnShape( newElem, aShapeId );
1098     }
1099   aMesh->RemoveElement( tr1 );
1100   aMesh->RemoveElement( tr2 );
1101
1102   // remove middle node (9)
1103   GetMeshDS()->RemoveNode( N1[4] );
1104
1105   return true;
1106 }
1107
1108 //=======================================================================
1109 //function : Reorient
1110 //purpose  : Reverse theElement orientation
1111 //=======================================================================
1112
1113 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
1114 {
1115   myLastCreatedElems.Clear();
1116   myLastCreatedNodes.Clear();
1117
1118   if (!theElem)
1119     return false;
1120   SMDS_ElemIteratorPtr it = theElem->nodesIterator();
1121   if ( !it || !it->more() )
1122     return false;
1123
1124   const SMDSAbs_ElementType type = theElem->GetType();
1125   if ( type < SMDSAbs_Edge || type > SMDSAbs_Volume )
1126     return false;
1127
1128   const SMDSAbs_EntityType geomType = theElem->GetEntityType();
1129   if ( geomType == SMDSEntity_Polyhedra ) // polyhedron
1130   {
1131     const SMDS_VtkVolume* aPolyedre =
1132       dynamic_cast<const SMDS_VtkVolume*>( theElem );
1133     if (!aPolyedre) {
1134       MESSAGE("Warning: bad volumic element");
1135       return false;
1136     }
1137     const int nbFaces = aPolyedre->NbFaces();
1138     vector<const SMDS_MeshNode *> poly_nodes;
1139     vector<int> quantities (nbFaces);
1140
1141     // reverse each face of the polyedre
1142     for (int iface = 1; iface <= nbFaces; iface++) {
1143       int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
1144       quantities[iface - 1] = nbFaceNodes;
1145
1146       for (inode = nbFaceNodes; inode >= 1; inode--) {
1147         const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
1148         poly_nodes.push_back(curNode);
1149       }
1150     }
1151     return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
1152   }
1153   else // other elements
1154   {
1155     vector<const SMDS_MeshNode*> nodes( theElem->begin_nodes(), theElem->end_nodes() );
1156     const std::vector<int>& interlace = SMDS_MeshCell::reverseSmdsOrder( geomType, nodes.size() );
1157     if ( interlace.empty() )
1158     {
1159       std::reverse( nodes.begin(), nodes.end() ); // obsolete, just in case
1160     }
1161     else
1162     {
1163       SMDS_MeshCell::applyInterlace( interlace, nodes );
1164     }
1165     return GetMeshDS()->ChangeElementNodes( theElem, &nodes[0], nodes.size() );
1166   }
1167   return false;
1168 }
1169
1170 //================================================================================
1171 /*!
1172  * \brief Reorient faces.
1173  * \param theFaces - the faces to reorient. If empty the whole mesh is meant
1174  * \param theDirection - desired direction of normal of \a theFace
1175  * \param theFace - one of \a theFaces that sould be oriented according to
1176  *        \a theDirection and whose orientation defines orientation of other faces
1177  * \return number of reoriented faces.
1178  */
1179 //================================================================================
1180
1181 int SMESH_MeshEditor::Reorient2D (TIDSortedElemSet &       theFaces,
1182                                   const gp_Dir&            theDirection,
1183                                   const SMDS_MeshElement * theFace)
1184 {
1185   int nbReori = 0;
1186   if ( !theFace || theFace->GetType() != SMDSAbs_Face ) return nbReori;
1187
1188   if ( theFaces.empty() )
1189   {
1190     SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator(/*idInceasingOrder=*/true);
1191     while ( fIt->more() )
1192       theFaces.insert( theFaces.end(), fIt->next() );
1193   }
1194
1195   // orient theFace according to theDirection
1196   gp_XYZ normal;
1197   SMESH_MeshAlgos::FaceNormal( theFace, normal, /*normalized=*/false );
1198   if ( normal * theDirection.XYZ() < 0 )
1199     nbReori += Reorient( theFace );
1200
1201   // Orient other faces
1202
1203   set< const SMDS_MeshElement* > startFaces, visitedFaces;
1204   TIDSortedElemSet avoidSet;
1205   set< SMESH_TLink > checkedLinks;
1206   pair< set< SMESH_TLink >::iterator, bool > linkIt_isNew;
1207
1208   if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces
1209     theFaces.erase( theFace );
1210   startFaces.insert( theFace );
1211
1212   int nodeInd1, nodeInd2;
1213   const SMDS_MeshElement*           otherFace;
1214   vector< const SMDS_MeshElement* > facesNearLink;
1215   vector< std::pair< int, int > >   nodeIndsOfFace;
1216
1217   set< const SMDS_MeshElement* >::iterator startFace = startFaces.begin();
1218   while ( !startFaces.empty() )
1219   {
1220     startFace = startFaces.begin();
1221     theFace = *startFace;
1222     startFaces.erase( startFace );
1223     if ( !visitedFaces.insert( theFace ).second )
1224       continue;
1225
1226     avoidSet.clear();
1227     avoidSet.insert(theFace);
1228
1229     NLink link( theFace->GetNode( 0 ), (SMDS_MeshNode *) 0 );
1230
1231     const int nbNodes = theFace->NbCornerNodes();
1232     for ( int i = 0; i < nbNodes; ++i ) // loop on links of theFace
1233     {
1234       link.second = theFace->GetNode(( i+1 ) % nbNodes );
1235       linkIt_isNew = checkedLinks.insert( link );
1236       if ( !linkIt_isNew.second )
1237       {
1238         // link has already been checked and won't be encountered more
1239         // if the group (theFaces) is manifold
1240         //checkedLinks.erase( linkIt_isNew.first );
1241       }
1242       else
1243       {
1244         facesNearLink.clear();
1245         nodeIndsOfFace.clear();
1246         while (( otherFace = SMESH_MeshAlgos::FindFaceInSet( link.first, link.second,
1247                                                              theFaces, avoidSet,
1248                                                              &nodeInd1, &nodeInd2 )))
1249           if ( otherFace != theFace)
1250           {
1251             facesNearLink.push_back( otherFace );
1252             nodeIndsOfFace.push_back( make_pair( nodeInd1, nodeInd2 ));
1253             avoidSet.insert( otherFace );
1254           }
1255         if ( facesNearLink.size() > 1 )
1256         {
1257           // NON-MANIFOLD mesh shell !
1258           // select a face most co-directed with theFace,
1259           // other faces won't be visited this time
1260           gp_XYZ NF, NOF;
1261           SMESH_MeshAlgos::FaceNormal( theFace, NF, /*normalized=*/false );
1262           double proj, maxProj = -1;
1263           for ( size_t i = 0; i < facesNearLink.size(); ++i ) {
1264             SMESH_MeshAlgos::FaceNormal( facesNearLink[i], NOF, /*normalized=*/false );
1265             if (( proj = Abs( NF * NOF )) > maxProj ) {
1266               maxProj = proj;
1267               otherFace = facesNearLink[i];
1268               nodeInd1  = nodeIndsOfFace[i].first;
1269               nodeInd2  = nodeIndsOfFace[i].second;
1270             }
1271           }
1272           // not to visit rejected faces
1273           for ( size_t i = 0; i < facesNearLink.size(); ++i )
1274             if ( facesNearLink[i] != otherFace && theFaces.size() > 1 )
1275               visitedFaces.insert( facesNearLink[i] );
1276         }
1277         else if ( facesNearLink.size() == 1 )
1278         {
1279           otherFace = facesNearLink[0];
1280           nodeInd1  = nodeIndsOfFace.back().first;
1281           nodeInd2  = nodeIndsOfFace.back().second;
1282         }
1283         if ( otherFace && otherFace != theFace)
1284         {
1285           // link must be reverse in otherFace if orientation ot otherFace
1286           // is same as that of theFace
1287           if ( abs(nodeInd2-nodeInd1) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1288           {
1289             nbReori += Reorient( otherFace );
1290           }
1291           startFaces.insert( otherFace );
1292         }
1293       }
1294       std::swap( link.first, link.second ); // reverse the link
1295     }
1296   }
1297   return nbReori;
1298 }
1299
1300 //================================================================================
1301 /*!
1302  * \brief Reorient faces basing on orientation of adjacent volumes.
1303  * \param theFaces - faces to reorient. If empty, all mesh faces are treated.
1304  * \param theVolumes - reference volumes.
1305  * \param theOutsideNormal - to orient faces to have their normal
1306  *        pointing either \a outside or \a inside the adjacent volumes.
1307  * \return number of reoriented faces.
1308  */
1309 //================================================================================
1310
1311 int SMESH_MeshEditor::Reorient2DBy3D (TIDSortedElemSet & theFaces,
1312                                       TIDSortedElemSet & theVolumes,
1313                                       const bool         theOutsideNormal)
1314 {
1315   int nbReori = 0;
1316
1317   SMDS_ElemIteratorPtr faceIt;
1318   if ( theFaces.empty() )
1319     faceIt = GetMeshDS()->elementsIterator( SMDSAbs_Face );
1320   else
1321     faceIt = elemSetIterator( theFaces );
1322
1323   vector< const SMDS_MeshNode* > faceNodes;
1324   TIDSortedElemSet checkedVolumes;
1325   set< const SMDS_MeshNode* > faceNodesSet;
1326   SMDS_VolumeTool volumeTool;
1327
1328   while ( faceIt->more() ) // loop on given faces
1329   {
1330     const SMDS_MeshElement* face = faceIt->next();
1331     if ( face->GetType() != SMDSAbs_Face )
1332       continue;
1333
1334     const size_t nbCornersNodes = face->NbCornerNodes();
1335     faceNodes.assign( face->begin_nodes(), face->end_nodes() );
1336
1337     checkedVolumes.clear();
1338     SMDS_ElemIteratorPtr vIt = faceNodes[ 0 ]->GetInverseElementIterator( SMDSAbs_Volume );
1339     while ( vIt->more() )
1340     {
1341       const SMDS_MeshElement* volume = vIt->next();
1342
1343       if ( !checkedVolumes.insert( volume ).second )
1344         continue;
1345       if ( !theVolumes.empty() && !theVolumes.count( volume ))
1346         continue;
1347
1348       // is volume adjacent?
1349       bool allNodesCommon = true;
1350       for ( size_t iN = 1; iN < nbCornersNodes && allNodesCommon; ++iN )
1351         allNodesCommon = ( volume->GetNodeIndex( faceNodes[ iN ]) > -1 );
1352       if ( !allNodesCommon )
1353         continue;
1354
1355       // get nodes of a corresponding volume facet
1356       faceNodesSet.clear();
1357       faceNodesSet.insert( faceNodes.begin(), faceNodes.end() );
1358       volumeTool.Set( volume );
1359       int facetID = volumeTool.GetFaceIndex( faceNodesSet );
1360       if ( facetID < 0 ) continue;
1361       volumeTool.SetExternalNormal();
1362       const SMDS_MeshNode** facetNodes = volumeTool.GetFaceNodes( facetID );
1363
1364       // compare order of faceNodes and facetNodes
1365       const int iQ = 1 + ( nbCornersNodes < faceNodes.size() );
1366       int iNN[2];
1367       for ( int i = 0; i < 2; ++i )
1368       {
1369         const SMDS_MeshNode* n = facetNodes[ i*iQ ];
1370         for ( size_t iN = 0; iN < nbCornersNodes; ++iN )
1371           if ( faceNodes[ iN ] == n )
1372           {
1373             iNN[ i ] = iN;
1374             break;
1375           }
1376       }
1377       bool isOutside = Abs( iNN[0]-iNN[1] ) == 1 ? iNN[0] < iNN[1] : iNN[0] > iNN[1];
1378       if ( isOutside != theOutsideNormal )
1379         nbReori += Reorient( face );
1380     }
1381   }  // loop on given faces
1382
1383   return nbReori;
1384 }
1385
1386 //=======================================================================
1387 //function : getBadRate
1388 //purpose  :
1389 //=======================================================================
1390
1391 static double getBadRate (const SMDS_MeshElement*               theElem,
1392                           SMESH::Controls::NumericalFunctorPtr& theCrit)
1393 {
1394   SMESH::Controls::TSequenceOfXYZ P;
1395   if ( !theElem || !theCrit->GetPoints( theElem, P ))
1396     return 1e100;
1397   return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1398   //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1399 }
1400
1401 //=======================================================================
1402 //function : QuadToTri
1403 //purpose  : Cut quadrangles into triangles.
1404 //           theCrit is used to select a diagonal to cut
1405 //=======================================================================
1406
1407 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet &                   theElems,
1408                                   SMESH::Controls::NumericalFunctorPtr theCrit)
1409 {
1410   myLastCreatedElems.Clear();
1411   myLastCreatedNodes.Clear();
1412
1413   if ( !theCrit.get() )
1414     return false;
1415
1416   SMESHDS_Mesh * aMesh = GetMeshDS();
1417
1418   Handle(Geom_Surface) surface;
1419   SMESH_MesherHelper   helper( *GetMesh() );
1420
1421   TIDSortedElemSet::iterator itElem;
1422   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
1423   {
1424     const SMDS_MeshElement* elem = *itElem;
1425     if ( !elem || elem->GetType() != SMDSAbs_Face )
1426       continue;
1427     if ( elem->NbCornerNodes() != 4 )
1428       continue;
1429
1430     // retrieve element nodes
1431     vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1432
1433     // compare two sets of possible triangles
1434     double aBadRate1, aBadRate2; // to what extent a set is bad
1435     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1436     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1437     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1438
1439     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1440     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1441     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1442
1443     const int aShapeId = FindShape( elem );
1444     const SMDS_MeshElement* newElem1 = 0;
1445     const SMDS_MeshElement* newElem2 = 0;
1446
1447     if ( !elem->IsQuadratic() ) // split liner quadrangle
1448     {
1449       // for MaxElementLength2D functor we return minimum diagonal for splitting,
1450       // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1451       if ( aBadRate1 <= aBadRate2 ) {
1452         // tr1 + tr2 is better
1453         newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1454         newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1455       }
1456       else {
1457         // tr3 + tr4 is better
1458         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1459         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1460       }
1461     }
1462     else // split quadratic quadrangle
1463     {
1464       helper.SetIsQuadratic( true );
1465       helper.SetIsBiQuadratic( aNodes.size() == 9 );
1466
1467       helper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem ));
1468       if ( aNodes.size() == 9 )
1469       {
1470         helper.SetIsBiQuadratic( true );
1471         if ( aBadRate1 <= aBadRate2 )
1472           helper.AddTLinkNode( aNodes[0], aNodes[2], aNodes[8] );
1473         else
1474           helper.AddTLinkNode( aNodes[1], aNodes[3], aNodes[8] );
1475       }
1476       // create a new element
1477       if ( aBadRate1 <= aBadRate2 ) {
1478         newElem1 = helper.AddFace( aNodes[2], aNodes[3], aNodes[0] );
1479         newElem2 = helper.AddFace( aNodes[2], aNodes[0], aNodes[1] );
1480       }
1481       else {
1482         newElem1 = helper.AddFace( aNodes[3], aNodes[0], aNodes[1] );
1483         newElem2 = helper.AddFace( aNodes[3], aNodes[1], aNodes[2] );
1484       }
1485     } // quadratic case
1486
1487     // care of a new element
1488
1489     myLastCreatedElems.Append(newElem1);
1490     myLastCreatedElems.Append(newElem2);
1491     AddToSameGroups( newElem1, elem, aMesh );
1492     AddToSameGroups( newElem2, elem, aMesh );
1493
1494     // put a new triangle on the same shape
1495     if ( aShapeId )
1496       aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1497     aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1498
1499     aMesh->RemoveElement( elem );
1500   }
1501   return true;
1502 }
1503
1504 //=======================================================================
1505 /*!
1506  * \brief Split each of given quadrangles into 4 triangles.
1507  * \param theElems - The faces to be splitted. If empty all faces are split.
1508  */
1509 //=======================================================================
1510
1511 void SMESH_MeshEditor::QuadTo4Tri (TIDSortedElemSet & theElems)
1512 {
1513   myLastCreatedElems.Clear();
1514   myLastCreatedNodes.Clear();
1515
1516   SMESH_MesherHelper helper( *GetMesh() );
1517   helper.SetElementsOnShape( true );
1518
1519   SMDS_ElemIteratorPtr faceIt;
1520   if ( theElems.empty() ) faceIt = GetMeshDS()->elementsIterator(SMDSAbs_Face);
1521   else                    faceIt = elemSetIterator( theElems );
1522
1523   bool   checkUV;
1524   gp_XY  uv [9]; uv[8] = gp_XY(0,0);
1525   gp_XYZ xyz[9];
1526   vector< const SMDS_MeshNode* > nodes;
1527   SMESHDS_SubMesh*               subMeshDS = 0;
1528   TopoDS_Face                    F;
1529   Handle(Geom_Surface)           surface;
1530   TopLoc_Location                loc;
1531
1532   while ( faceIt->more() )
1533   {
1534     const SMDS_MeshElement* quad = faceIt->next();
1535     if ( !quad || quad->NbCornerNodes() != 4 )
1536       continue;
1537
1538     // get a surface the quad is on
1539
1540     if ( quad->getshapeId() < 1 )
1541     {
1542       F.Nullify();
1543       helper.SetSubShape( 0 );
1544       subMeshDS = 0;
1545     }
1546     else if ( quad->getshapeId() != helper.GetSubShapeID() )
1547     {
1548       helper.SetSubShape( quad->getshapeId() );
1549       if ( !helper.GetSubShape().IsNull() &&
1550            helper.GetSubShape().ShapeType() == TopAbs_FACE )
1551       {
1552         F = TopoDS::Face( helper.GetSubShape() );
1553         surface = BRep_Tool::Surface( F, loc );
1554         subMeshDS = GetMeshDS()->MeshElements( quad->getshapeId() );
1555       }
1556       else
1557       {
1558         helper.SetSubShape( 0 );
1559         subMeshDS = 0;
1560       }
1561     }
1562
1563     // create a central node
1564
1565     const SMDS_MeshNode* nCentral;
1566     nodes.assign( quad->begin_nodes(), quad->end_nodes() );
1567
1568     if ( nodes.size() == 9 )
1569     {
1570       nCentral = nodes.back();
1571     }
1572     else
1573     {
1574       size_t iN = 0;
1575       if ( F.IsNull() )
1576       {
1577         for ( ; iN < nodes.size(); ++iN )
1578           xyz[ iN ] = SMESH_TNodeXYZ( nodes[ iN ] );
1579
1580         for ( ; iN < 8; ++iN ) // mid-side points of a linear qudrangle
1581           xyz[ iN ] = 0.5 * ( xyz[ iN - 4 ] + xyz[( iN - 3 )%4 ] );
1582
1583         xyz[ 8 ] = helper.calcTFI( 0.5, 0.5,
1584                                    xyz[0], xyz[1], xyz[2], xyz[3],
1585                                    xyz[4], xyz[5], xyz[6], xyz[7] );
1586       }
1587       else
1588       {
1589         for ( ; iN < nodes.size(); ++iN )
1590           uv[ iN ] = helper.GetNodeUV( F, nodes[iN], nodes[(iN+2)%4], &checkUV );
1591
1592         for ( ; iN < 8; ++iN ) // UV of mid-side points of a linear qudrangle
1593           uv[ iN ] = helper.GetMiddleUV( surface, uv[ iN - 4 ], uv[( iN - 3 )%4 ] );
1594
1595         uv[ 8 ] = helper.calcTFI( 0.5, 0.5,
1596                                   uv[0], uv[1], uv[2], uv[3],
1597                                   uv[4], uv[5], uv[6], uv[7] );
1598
1599         gp_Pnt p = surface->Value( uv[8].X(), uv[8].Y() ).Transformed( loc );
1600         xyz[ 8 ] = p.XYZ();
1601       }
1602
1603       nCentral = helper.AddNode( xyz[8].X(), xyz[8].Y(), xyz[8].Z(), /*id=*/0,
1604                                  uv[8].X(), uv[8].Y() );
1605       myLastCreatedNodes.Append( nCentral );
1606     }
1607
1608     // create 4 triangles
1609
1610     helper.SetIsQuadratic  ( nodes.size() > 4 );
1611     helper.SetIsBiQuadratic( nodes.size() == 9 );
1612     if ( helper.GetIsQuadratic() )
1613       helper.AddTLinks( static_cast< const SMDS_MeshFace*>( quad ));
1614
1615     GetMeshDS()->RemoveFreeElement( quad, subMeshDS, /*fromGroups=*/false );
1616
1617     for ( int i = 0; i < 4; ++i )
1618     {
1619       SMDS_MeshElement* tria = helper.AddFace( nodes[ i ],
1620                                                nodes[(i+1)%4],
1621                                                nCentral );
1622       ReplaceElemInGroups( tria, quad, GetMeshDS() );
1623       myLastCreatedElems.Append( tria );
1624     }
1625   }
1626 }
1627
1628 //=======================================================================
1629 //function : BestSplit
1630 //purpose  : Find better diagonal for cutting.
1631 //=======================================================================
1632
1633 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement*              theQuad,
1634                                  SMESH::Controls::NumericalFunctorPtr theCrit)
1635 {
1636   myLastCreatedElems.Clear();
1637   myLastCreatedNodes.Clear();
1638
1639   if (!theCrit.get())
1640     return -1;
1641
1642   if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1643     return -1;
1644
1645   if( theQuad->NbNodes()==4 ||
1646       (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1647
1648     // retrieve element nodes
1649     const SMDS_MeshNode* aNodes [4];
1650     SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1651     int i = 0;
1652     //while (itN->more())
1653     while (i<4) {
1654       aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1655     }
1656     // compare two sets of possible triangles
1657     double aBadRate1, aBadRate2; // to what extent a set is bad
1658     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1659     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1660     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1661
1662     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1663     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1664     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1665     // for MaxElementLength2D functor we return minimum diagonal for splitting,
1666     // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1667     if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1668       return 1; // diagonal 1-3
1669
1670     return 2; // diagonal 2-4
1671   }
1672   return -1;
1673 }
1674
1675 namespace
1676 {
1677   // Methods of splitting volumes into tetra
1678
1679   const int theHexTo5_1[5*4+1] =
1680     {
1681       0, 1, 2, 5,    0, 4, 5, 7,     0, 2, 3, 7,    2, 5, 6, 7,     0, 5, 2, 7,   -1
1682     };
1683   const int theHexTo5_2[5*4+1] =
1684     {
1685       1, 2, 3, 6,    1, 4, 5, 6,     0, 1, 3, 4,    3, 4, 6, 7,     1, 3, 4, 6,   -1
1686     };
1687   const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1688
1689   const int theHexTo6_1[6*4+1] =
1690     {
1691       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
1692     };
1693   const int theHexTo6_2[6*4+1] =
1694     {
1695       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
1696     };
1697   const int theHexTo6_3[6*4+1] =
1698     {
1699       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
1700     };
1701   const int theHexTo6_4[6*4+1] =
1702     {
1703       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
1704     };
1705   const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1706
1707   const int thePyraTo2_1[2*4+1] =
1708     {
1709       0, 1, 2, 4,    0, 2, 3, 4,   -1
1710     };
1711   const int thePyraTo2_2[2*4+1] =
1712     {
1713       1, 2, 3, 4,    1, 3, 0, 4,   -1
1714     };
1715   const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1716
1717   const int thePentaTo3_1[3*4+1] =
1718     {
1719       0, 1, 2, 3,    1, 3, 4, 2,     2, 3, 4, 5,    -1
1720     };
1721   const int thePentaTo3_2[3*4+1] =
1722     {
1723       1, 2, 0, 4,    2, 4, 5, 0,     0, 4, 5, 3,    -1
1724     };
1725   const int thePentaTo3_3[3*4+1] =
1726     {
1727       2, 0, 1, 5,    0, 5, 3, 1,     1, 5, 3, 4,    -1
1728     };
1729   const int thePentaTo3_4[3*4+1] =
1730     {
1731       0, 1, 2, 3,    1, 3, 4, 5,     2, 3, 1, 5,    -1
1732     };
1733   const int thePentaTo3_5[3*4+1] =
1734     {
1735       1, 2, 0, 4,    2, 4, 5, 3,     0, 4, 2, 3,    -1
1736     };
1737   const int thePentaTo3_6[3*4+1] =
1738     {
1739       2, 0, 1, 5,    0, 5, 3, 4,     1, 5, 0, 4,    -1
1740     };
1741   const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1742                                 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1743
1744   // Methods of splitting hexahedron into prisms
1745
1746   const int theHexTo4Prisms_BT[6*4+1] = // bottom-top
1747     {
1748       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
1749     };
1750   const int theHexTo4Prisms_LR[6*4+1] = // left-right
1751     {
1752       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
1753     };
1754   const int theHexTo4Prisms_FB[6*4+1] = // front-back
1755     {
1756       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
1757     };
1758
1759   const int theHexTo2Prisms_BT_1[6*2+1] =
1760     {
1761       0, 1, 3, 4, 5, 7,    1, 2, 3, 5, 6, 7,   -1
1762     };
1763   const int theHexTo2Prisms_BT_2[6*2+1] =
1764     {
1765       0, 1, 2, 4, 5, 6,    0, 2, 3, 4, 6, 7,   -1
1766     };
1767   const int* theHexTo2Prisms_BT[2] = { theHexTo2Prisms_BT_1, theHexTo2Prisms_BT_2 };
1768
1769   const int theHexTo2Prisms_LR_1[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[6*2+1] =
1774     {
1775       1, 0, 4, 2, 3, 7,    1, 4, 5, 2, 7, 6,   -1
1776     };
1777   const int* theHexTo2Prisms_LR[2] = { theHexTo2Prisms_LR_1, theHexTo2Prisms_LR_2 };
1778
1779   const int theHexTo2Prisms_FB_1[6*2+1] =
1780     {
1781       0, 3, 4, 1, 2, 5,    3, 7, 4, 2, 6, 5,   -1
1782     };
1783   const int theHexTo2Prisms_FB_2[6*2+1] =
1784     {
1785       0, 3, 7, 1, 2, 7,    0, 7, 4, 1, 6, 5,   -1
1786     };
1787   const int* theHexTo2Prisms_FB[2] = { theHexTo2Prisms_FB_1, theHexTo2Prisms_FB_2 };
1788
1789
1790   struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1791   {
1792     int _n1, _n2, _n3;
1793     TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1794     bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1795     bool hasAdjacentVol( const SMDS_MeshElement*    elem,
1796                          const SMDSAbs_GeometryType geom = SMDSGeom_TETRA) const;
1797   };
1798   struct TSplitMethod
1799   {
1800     int        _nbSplits;
1801     int        _nbCorners;
1802     const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1803     bool       _baryNode;     //!< additional node is to be created at cell barycenter
1804     bool       _ownConn;      //!< to delete _connectivity in destructor
1805     map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1806
1807     TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1808       : _nbSplits(nbTet), _nbCorners(4), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1809     ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1810     bool hasFacet( const TTriangleFacet& facet ) const
1811     {
1812       if ( _nbCorners == 4 )
1813       {
1814         const int* tetConn = _connectivity;
1815         for ( ; tetConn[0] >= 0; tetConn += 4 )
1816           if (( facet.contains( tetConn[0] ) +
1817                 facet.contains( tetConn[1] ) +
1818                 facet.contains( tetConn[2] ) +
1819                 facet.contains( tetConn[3] )) == 3 )
1820             return true;
1821       }
1822       else // prism, _nbCorners == 6
1823       {
1824         const int* prismConn = _connectivity;
1825         for ( ; prismConn[0] >= 0; prismConn += 6 )
1826         {
1827           if (( facet.contains( prismConn[0] ) &&
1828                 facet.contains( prismConn[1] ) &&
1829                 facet.contains( prismConn[2] ))
1830               ||
1831               ( facet.contains( prismConn[3] ) &&
1832                 facet.contains( prismConn[4] ) &&
1833                 facet.contains( prismConn[5] )))
1834             return true;
1835         }
1836       }
1837       return false;
1838     }
1839   };
1840
1841   //=======================================================================
1842   /*!
1843    * \brief return TSplitMethod for the given element to split into tetrahedra
1844    */
1845   //=======================================================================
1846
1847   TSplitMethod getTetraSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1848   {
1849     const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1850
1851     // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1852     // an edge and a face barycenter; tertaherdons are based on triangles and
1853     // a volume barycenter
1854     const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1855
1856     // Find out how adjacent volumes are split
1857
1858     vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1859     int hasAdjacentSplits = 0, maxTetConnSize = 0;
1860     for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1861     {
1862       int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1863       maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1864       if ( nbNodes < 4 ) continue;
1865
1866       list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1867       const int* nInd = vol.GetFaceNodesIndices( iF );
1868       if ( nbNodes == 4 )
1869       {
1870         TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1871         TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1872         if      ( t012.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t012 );
1873         else if ( t123.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t123 );
1874       }
1875       else
1876       {
1877         int iCom = 0; // common node of triangle faces to split into
1878         for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1879         {
1880           TTriangleFacet t012( nInd[ iQ * ( iCom             )],
1881                                nInd[ iQ * ( (iCom+1)%nbNodes )],
1882                                nInd[ iQ * ( (iCom+2)%nbNodes )]);
1883           TTriangleFacet t023( nInd[ iQ * ( iCom             )],
1884                                nInd[ iQ * ( (iCom+2)%nbNodes )],
1885                                nInd[ iQ * ( (iCom+3)%nbNodes )]);
1886           if ( t012.hasAdjacentVol( vol.Element() ) && t023.hasAdjacentVol( vol.Element() ))
1887           {
1888             triaSplits.push_back( t012 );
1889             triaSplits.push_back( t023 );
1890             break;
1891           }
1892         }
1893       }
1894       if ( !triaSplits.empty() )
1895         hasAdjacentSplits = true;
1896     }
1897
1898     // Among variants of split method select one compliant with adjacent volumes
1899
1900     TSplitMethod method;
1901     if ( !vol.Element()->IsPoly() && !is24TetMode )
1902     {
1903       int nbVariants = 2, nbTet = 0;
1904       const int** connVariants = 0;
1905       switch ( vol.Element()->GetEntityType() )
1906       {
1907       case SMDSEntity_Hexa:
1908       case SMDSEntity_Quad_Hexa:
1909       case SMDSEntity_TriQuad_Hexa:
1910         if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1911           connVariants = theHexTo5, nbTet = 5;
1912         else
1913           connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1914         break;
1915       case SMDSEntity_Pyramid:
1916       case SMDSEntity_Quad_Pyramid:
1917         connVariants = thePyraTo2;  nbTet = 2;
1918         break;
1919       case SMDSEntity_Penta:
1920       case SMDSEntity_Quad_Penta:
1921         connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1922         break;
1923       default:
1924         nbVariants = 0;
1925       }
1926       for ( int variant = 0; variant < nbVariants && method._nbSplits == 0; ++variant )
1927       {
1928         // check method compliancy with adjacent tetras,
1929         // all found splits must be among facets of tetras described by this method
1930         method = TSplitMethod( nbTet, connVariants[variant] );
1931         if ( hasAdjacentSplits && method._nbSplits > 0 )
1932         {
1933           bool facetCreated = true;
1934           for ( size_t iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1935           {
1936             list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1937             for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1938               facetCreated = method.hasFacet( *facet );
1939           }
1940           if ( !facetCreated )
1941             method = TSplitMethod(0); // incompatible method
1942         }
1943       }
1944     }
1945     if ( method._nbSplits < 1 )
1946     {
1947       // No standard method is applicable, use a generic solution:
1948       // each facet of a volume is split into triangles and
1949       // each of triangles and a volume barycenter form a tetrahedron.
1950
1951       const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1952
1953       int* connectivity = new int[ maxTetConnSize + 1 ];
1954       method._connectivity = connectivity;
1955       method._ownConn = true;
1956       method._baryNode = !isHex27; // to create central node or not
1957
1958       int connSize = 0;
1959       int baryCenInd = vol.NbNodes() - int( isHex27 );
1960       for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1961       {
1962         const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1963         const int*   nInd = vol.GetFaceNodesIndices( iF );
1964         // find common node of triangle facets of tetra to create
1965         int iCommon = 0; // index in linear numeration
1966         const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1967         if ( !triaSplits.empty() )
1968         {
1969           // by found facets
1970           const TTriangleFacet* facet = &triaSplits.front();
1971           for ( ; iCommon < nbNodes-1 ; ++iCommon )
1972             if ( facet->contains( nInd[ iQ * iCommon ]) &&
1973                  facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1974               break;
1975         }
1976         else if ( nbNodes > 3 && !is24TetMode )
1977         {
1978           // find the best method of splitting into triangles by aspect ratio
1979           SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1980           map< double, int > badness2iCommon;
1981           const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1982           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1983           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1984           {
1985             double badness = 0;
1986             for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1987             {
1988               SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon         )],
1989                                       nodes[ iQ*((iLast-1)%nbNodes)],
1990                                       nodes[ iQ*((iLast  )%nbNodes)]);
1991               badness += getBadRate( &tria, aspectRatio );
1992             }
1993             badness2iCommon.insert( make_pair( badness, iCommon ));
1994           }
1995           // use iCommon with lowest badness
1996           iCommon = badness2iCommon.begin()->second;
1997         }
1998         if ( iCommon >= nbNodes )
1999           iCommon = 0; // something wrong
2000
2001         // fill connectivity of tetrahedra based on a current face
2002         int nbTet = nbNodes - 2;
2003         if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
2004         {
2005           int faceBaryCenInd;
2006           if ( isHex27 )
2007           {
2008             faceBaryCenInd = vol.GetCenterNodeIndex( iF );
2009             method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
2010           }
2011           else
2012           {
2013             method._faceBaryNode[ iF ] = 0;
2014             faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
2015           }
2016           nbTet = nbNodes;
2017           for ( int i = 0; i < nbTet; ++i )
2018           {
2019             int i1 = i, i2 = (i+1) % nbNodes;
2020             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2021             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2022             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2023             connectivity[ connSize++ ] = faceBaryCenInd;
2024             connectivity[ connSize++ ] = baryCenInd;
2025           }
2026         }
2027         else
2028         {
2029           for ( int i = 0; i < nbTet; ++i )
2030           {
2031             int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
2032             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2033             connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
2034             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2035             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2036             connectivity[ connSize++ ] = baryCenInd;
2037           }
2038         }
2039         method._nbSplits += nbTet;
2040
2041       } // loop on volume faces
2042
2043       connectivity[ connSize++ ] = -1;
2044
2045     } // end of generic solution
2046
2047     return method;
2048   }
2049   //=======================================================================
2050   /*!
2051    * \brief return TSplitMethod to split haxhedron into prisms
2052    */
2053   //=======================================================================
2054
2055   TSplitMethod getPrismSplitMethod( SMDS_VolumeTool& vol,
2056                                     const int        methodFlags,
2057                                     const int        facetToSplit)
2058   {
2059     // order of facets in HEX according to SMDS_VolumeTool::Hexa_F :
2060     // B, T, L, B, R, F
2061     const int iF = ( facetToSplit < 2 ) ? 0 : 1 + ( facetToSplit-2 ) % 2; // [0,1,2]
2062
2063     if ( methodFlags == SMESH_MeshEditor::HEXA_TO_4_PRISMS )
2064     {
2065       static TSplitMethod to4methods[4]; // order BT, LR, FB
2066       if ( to4methods[iF]._nbSplits == 0 )
2067       {
2068         switch ( iF ) {
2069         case 0:
2070           to4methods[iF]._connectivity = theHexTo4Prisms_BT;
2071           to4methods[iF]._faceBaryNode[ 0 ] = 0;
2072           to4methods[iF]._faceBaryNode[ 1 ] = 0;
2073           break;
2074         case 1:
2075           to4methods[iF]._connectivity = theHexTo4Prisms_LR;
2076           to4methods[iF]._faceBaryNode[ 2 ] = 0;
2077           to4methods[iF]._faceBaryNode[ 4 ] = 0;
2078           break;
2079         case 2:
2080           to4methods[iF]._connectivity = theHexTo4Prisms_FB;
2081           to4methods[iF]._faceBaryNode[ 3 ] = 0;
2082           to4methods[iF]._faceBaryNode[ 5 ] = 0;
2083           break;
2084         default: return to4methods[3];
2085         }
2086         to4methods[iF]._nbSplits  = 4;
2087         to4methods[iF]._nbCorners = 6;
2088       }
2089       return to4methods[iF];
2090     }
2091     // else if ( methodFlags == HEXA_TO_2_PRISMS )
2092
2093     TSplitMethod method;
2094
2095     const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2096
2097     const int nbVariants = 2, nbSplits = 2;
2098     const int** connVariants = 0;
2099     switch ( iF ) {
2100     case 0: connVariants = theHexTo2Prisms_BT; break;
2101     case 1: connVariants = theHexTo2Prisms_LR; break;
2102     case 2: connVariants = theHexTo2Prisms_FB; break;
2103     default: return method;
2104     }
2105
2106     // look for prisms adjacent via facetToSplit and an opposite one
2107     for ( int is2nd = 0; is2nd < 2; ++is2nd )
2108     {
2109       int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2110       int nbNodes = vol.NbFaceNodes( iFacet ) / iQ;
2111       if ( nbNodes != 4 ) return method;
2112
2113       const int* nInd = vol.GetFaceNodesIndices( iFacet );
2114       TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2115       TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2116       TTriangleFacet* t;
2117       if      ( t012.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2118         t = &t012;
2119       else if ( t123.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2120         t = &t123;
2121       else
2122         continue;
2123
2124       // there are adjacent prism
2125       for ( int variant = 0; variant < nbVariants; ++variant )
2126       {
2127         // check method compliancy with adjacent prisms,
2128         // the found prism facets must be among facets of prisms described by current method
2129         method._nbSplits     = nbSplits;
2130         method._nbCorners    = 6;
2131         method._connectivity = connVariants[ variant ];
2132         if ( method.hasFacet( *t ))
2133           return method;
2134       }
2135     }
2136
2137     // No adjacent prisms. Select a variant with a best aspect ratio.
2138
2139     double badness[2] = { 0., 0. };
2140     static SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
2141     const SMDS_MeshNode** nodes = vol.GetNodes();
2142     for ( int variant = 0; variant < nbVariants; ++variant )
2143       for ( int is2nd = 0; is2nd < 2; ++is2nd )
2144       {
2145         int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2146         const int*             nInd = vol.GetFaceNodesIndices( iFacet );
2147
2148         method._connectivity = connVariants[ variant ];
2149         TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2150         TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2151         TTriangleFacet* t = ( method.hasFacet( t012 )) ? & t012 : & t123;
2152
2153         SMDS_FaceOfNodes tria ( nodes[ t->_n1 ],
2154                                 nodes[ t->_n2 ],
2155                                 nodes[ t->_n3 ] );
2156         badness[ variant ] += getBadRate( &tria, aspectRatio );
2157       }
2158     const int iBetter = ( badness[1] < badness[0] && badness[0]-badness[1] > 0.1 * badness[0] );
2159
2160     method._nbSplits     = nbSplits;
2161     method._nbCorners    = 6;
2162     method._connectivity = connVariants[ iBetter ];
2163
2164     return method;
2165   }
2166
2167   //================================================================================
2168   /*!
2169    * \brief Check if there is a tetraherdon adjacent to the given element via this facet
2170    */
2171   //================================================================================
2172
2173   bool TTriangleFacet::hasAdjacentVol( const SMDS_MeshElement*    elem,
2174                                        const SMDSAbs_GeometryType geom ) const
2175   {
2176     // find the tetrahedron including the three nodes of facet
2177     const SMDS_MeshNode* n1 = elem->GetNode(_n1);
2178     const SMDS_MeshNode* n2 = elem->GetNode(_n2);
2179     const SMDS_MeshNode* n3 = elem->GetNode(_n3);
2180     SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
2181     while ( volIt1->more() )
2182     {
2183       const SMDS_MeshElement* v = volIt1->next();
2184       if ( v->GetGeomType() != geom )
2185         continue;
2186       const int lastCornerInd = v->NbCornerNodes() - 1;
2187       if ( v->IsQuadratic() && v->GetNodeIndex( n1 ) > lastCornerInd )
2188         continue; // medium node not allowed
2189       const int ind2 = v->GetNodeIndex( n2 );
2190       if ( ind2 < 0 || lastCornerInd < ind2 )
2191         continue;
2192       const int ind3 = v->GetNodeIndex( n3 );
2193       if ( ind3 < 0 || lastCornerInd < ind3 )
2194         continue;
2195       return true;
2196     }
2197     return false;
2198   }
2199
2200   //=======================================================================
2201   /*!
2202    * \brief A key of a face of volume
2203    */
2204   //=======================================================================
2205
2206   struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
2207   {
2208     TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
2209     {
2210       TIDSortedNodeSet sortedNodes;
2211       const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2212       int nbNodes = vol.NbFaceNodes( iF );
2213       const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
2214       for ( int i = 0; i < nbNodes; i += iQ )
2215         sortedNodes.insert( fNodes[i] );
2216       TIDSortedNodeSet::iterator n = sortedNodes.begin();
2217       first.first   = (*(n++))->GetID();
2218       first.second  = (*(n++))->GetID();
2219       second.first  = (*(n++))->GetID();
2220       second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
2221     }
2222   };
2223 } // namespace
2224
2225 //=======================================================================
2226 //function : SplitVolumes
2227 //purpose  : Split volume elements into tetrahedra or prisms.
2228 //           If facet ID < 0, element is split into tetrahedra,
2229 //           else a hexahedron is split into prisms so that the given facet is
2230 //           split into triangles
2231 //=======================================================================
2232
2233 void SMESH_MeshEditor::SplitVolumes (const TFacetOfElem & theElems,
2234                                      const int            theMethodFlags)
2235 {
2236   SMDS_VolumeTool    volTool;
2237   SMESH_MesherHelper helper( *GetMesh()), fHelper(*GetMesh());
2238   fHelper.ToFixNodeParameters( true );
2239
2240   SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
2241   SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
2242
2243   SMESH_SequenceOfElemPtr newNodes, newElems;
2244
2245   // map face of volume to it's baricenrtic node
2246   map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
2247   double bc[3];
2248   vector<const SMDS_MeshElement* > splitVols;
2249
2250   TFacetOfElem::const_iterator elem2facet = theElems.begin();
2251   for ( ; elem2facet != theElems.end(); ++elem2facet )
2252   {
2253     const SMDS_MeshElement* elem = elem2facet->first;
2254     const int       facetToSplit = elem2facet->second;
2255     if ( elem->GetType() != SMDSAbs_Volume )
2256       continue;
2257     const SMDSAbs_EntityType geomType = elem->GetEntityType();
2258     if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
2259       continue;
2260
2261     if ( !volTool.Set( elem, /*ignoreCentralNodes=*/false )) continue; // strange...
2262
2263     TSplitMethod splitMethod = ( facetToSplit < 0  ?
2264                                  getTetraSplitMethod( volTool, theMethodFlags ) :
2265                                  getPrismSplitMethod( volTool, theMethodFlags, facetToSplit ));
2266     if ( splitMethod._nbSplits < 1 ) continue;
2267
2268     // find submesh to add new tetras to
2269     if ( !subMesh || !subMesh->Contains( elem ))
2270     {
2271       int shapeID = FindShape( elem );
2272       helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
2273       subMesh = GetMeshDS()->MeshElements( shapeID );
2274     }
2275     int iQ;
2276     if ( elem->IsQuadratic() )
2277     {
2278       iQ = 2;
2279       // add quadratic links to the helper
2280       for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2281       {
2282         const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
2283         int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
2284         for ( int iN = 0; iN < nbN; iN += iQ )
2285           helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
2286       }
2287       helper.SetIsQuadratic( true );
2288     }
2289     else
2290     {
2291       iQ = 1;
2292       helper.SetIsQuadratic( false );
2293     }
2294     vector<const SMDS_MeshNode*> nodes( volTool.GetNodes(),
2295                                         volTool.GetNodes() + elem->NbNodes() );
2296     helper.SetElementsOnShape( true );
2297     if ( splitMethod._baryNode )
2298     {
2299       // make a node at barycenter
2300       volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
2301       SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
2302       nodes.push_back( gcNode );
2303       newNodes.Append( gcNode );
2304     }
2305     if ( !splitMethod._faceBaryNode.empty() )
2306     {
2307       // make or find baricentric nodes of faces
2308       map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
2309       for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
2310       {
2311         map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
2312           volFace2BaryNode.insert
2313           ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
2314         if ( !f_n->second )
2315         {
2316           volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
2317           newNodes.Append( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
2318         }
2319         nodes.push_back( iF_n->second = f_n->second );
2320       }
2321     }
2322
2323     // make new volumes
2324     splitVols.resize( splitMethod._nbSplits ); // splits of a volume
2325     const int* volConn = splitMethod._connectivity;
2326     if ( splitMethod._nbCorners == 4 ) // tetra
2327       for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2328         newElems.Append( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2329                                                             nodes[ volConn[1] ],
2330                                                             nodes[ volConn[2] ],
2331                                                             nodes[ volConn[3] ]));
2332     else // prisms
2333       for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2334         newElems.Append( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2335                                                             nodes[ volConn[1] ],
2336                                                             nodes[ volConn[2] ],
2337                                                             nodes[ volConn[3] ],
2338                                                             nodes[ volConn[4] ],
2339                                                             nodes[ volConn[5] ]));
2340
2341     ReplaceElemInGroups( elem, splitVols, GetMeshDS() );
2342
2343     // Split faces on sides of the split volume
2344
2345     const SMDS_MeshNode** volNodes = volTool.GetNodes();
2346     for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2347     {
2348       const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
2349       if ( nbNodes < 4 ) continue;
2350
2351       // find an existing face
2352       vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
2353                                            volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
2354       while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
2355                                                                        /*noMedium=*/false))
2356       {
2357         // make triangles
2358         helper.SetElementsOnShape( false );
2359         vector< const SMDS_MeshElement* > triangles;
2360
2361         // find submesh to add new triangles in
2362         if ( !fSubMesh || !fSubMesh->Contains( face ))
2363         {
2364           int shapeID = FindShape( face );
2365           fSubMesh = GetMeshDS()->MeshElements( shapeID );
2366         }
2367         map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
2368         if ( iF_n != splitMethod._faceBaryNode.end() )
2369         {
2370           const SMDS_MeshNode *baryNode = iF_n->second;
2371           for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
2372           {
2373             const SMDS_MeshNode* n1 = fNodes[iN];
2374             const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
2375             const SMDS_MeshNode *n3 = baryNode;
2376             if ( !volTool.IsFaceExternal( iF ))
2377               swap( n2, n3 );
2378             triangles.push_back( helper.AddFace( n1,n2,n3 ));
2379           }
2380           if ( fSubMesh ) // update position of the bary node on geometry
2381           {
2382             if ( subMesh )
2383               subMesh->RemoveNode( baryNode, false );
2384             GetMeshDS()->SetNodeOnFace( baryNode, fSubMesh->GetID() );
2385             const TopoDS_Shape& s = GetMeshDS()->IndexToShape( fSubMesh->GetID() );
2386             if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE )
2387             {
2388               fHelper.SetSubShape( s );
2389               gp_XY uv( 1e100, 1e100 );
2390               double distXYZ[4];
2391               if ( !fHelper.CheckNodeUV( TopoDS::Face( s ), baryNode,
2392                                         uv, /*tol=*/1e-7, /*force=*/true, distXYZ ) &&
2393                    uv.X() < 1e100 )
2394               {
2395                 // node is too far from the surface
2396                 GetMeshDS()->MoveNode( baryNode, distXYZ[1], distXYZ[2], distXYZ[3] );
2397                 const_cast<SMDS_MeshNode*>( baryNode )->SetPosition
2398                   ( SMDS_PositionPtr( new SMDS_FacePosition( uv.X(), uv.Y() )));
2399               }
2400             }
2401           }
2402         }
2403         else
2404         {
2405           // among possible triangles create ones discribed by split method
2406           const int* nInd = volTool.GetFaceNodesIndices( iF );
2407           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2408           int iCom = 0; // common node of triangle faces to split into
2409           list< TTriangleFacet > facets;
2410           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
2411           {
2412             TTriangleFacet t012( nInd[ iQ * ( iCom                )],
2413                                  nInd[ iQ * ( (iCom+1)%nbNodes )],
2414                                  nInd[ iQ * ( (iCom+2)%nbNodes )]);
2415             TTriangleFacet t023( nInd[ iQ * ( iCom                )],
2416                                  nInd[ iQ * ( (iCom+2)%nbNodes )],
2417                                  nInd[ iQ * ( (iCom+3)%nbNodes )]);
2418             if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
2419             {
2420               facets.push_back( t012 );
2421               facets.push_back( t023 );
2422               for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
2423                 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom             )],
2424                                                   nInd[ iQ * ((iLast-1)%nbNodes )],
2425                                                   nInd[ iQ * ((iLast  )%nbNodes )]));
2426               break;
2427             }
2428           }
2429           list< TTriangleFacet >::iterator facet = facets.begin();
2430           if ( facet == facets.end() )
2431             break;
2432           for ( ; facet != facets.end(); ++facet )
2433           {
2434             if ( !volTool.IsFaceExternal( iF ))
2435               swap( facet->_n2, facet->_n3 );
2436             triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
2437                                                  volNodes[ facet->_n2 ],
2438                                                  volNodes[ facet->_n3 ]));
2439           }
2440         }
2441         for ( size_t i = 0; i < triangles.size(); ++i )
2442         {
2443           if ( !triangles[ i ]) continue;
2444           if ( fSubMesh )
2445             fSubMesh->AddElement( triangles[ i ]);
2446           newElems.Append( triangles[ i ]);
2447         }
2448         ReplaceElemInGroups( face, triangles, GetMeshDS() );
2449         GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2450
2451       } // while a face based on facet nodes exists
2452     } // loop on volume faces to split them into triangles
2453
2454     GetMeshDS()->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2455
2456     if ( geomType == SMDSEntity_TriQuad_Hexa )
2457     {
2458       // remove medium nodes that could become free
2459       for ( int i = 20; i < volTool.NbNodes(); ++i )
2460         if ( volNodes[i]->NbInverseElements() == 0 )
2461           GetMeshDS()->RemoveNode( volNodes[i] );
2462     }
2463   } // loop on volumes to split
2464
2465   myLastCreatedNodes = newNodes;
2466   myLastCreatedElems = newElems;
2467 }
2468
2469 //=======================================================================
2470 //function : GetHexaFacetsToSplit
2471 //purpose  : For hexahedra that will be split into prisms, finds facets to
2472 //           split into triangles. Only hexahedra adjacent to the one closest
2473 //           to theFacetNormal.Location() are returned.
2474 //param [in,out] theHexas - the hexahedra
2475 //param [in]     theFacetNormal - facet normal
2476 //param [out]    theFacets - the hexahedra and found facet IDs
2477 //=======================================================================
2478
2479 void SMESH_MeshEditor::GetHexaFacetsToSplit( TIDSortedElemSet& theHexas,
2480                                              const gp_Ax1&     theFacetNormal,
2481                                              TFacetOfElem &    theFacets)
2482 {
2483   #define THIS_METHOD "SMESH_MeshEditor::GetHexaFacetsToSplit(): "
2484
2485   // Find a hexa closest to the location of theFacetNormal
2486
2487   const SMDS_MeshElement* startHex;
2488   {
2489     // get SMDS_ElemIteratorPtr on theHexas
2490     typedef const SMDS_MeshElement*                                      TValue;
2491     typedef TIDSortedElemSet::iterator                                   TSetIterator;
2492     typedef SMDS::SimpleAccessor<TValue,TSetIterator>                    TAccesor;
2493     typedef SMDS_MeshElement::GeomFilter                                 TFilter;
2494     typedef SMDS_SetIterator < TValue, TSetIterator, TAccesor, TFilter > TElemSetIter;
2495     SMDS_ElemIteratorPtr elemIt = SMDS_ElemIteratorPtr
2496       ( new TElemSetIter( theHexas.begin(),
2497                           theHexas.end(),
2498                           SMDS_MeshElement::GeomFilter( SMDSGeom_HEXA )));
2499
2500     SMESH_ElementSearcher* searcher =
2501       SMESH_MeshAlgos::GetElementSearcher( *myMesh->GetMeshDS(), elemIt );
2502
2503     startHex = searcher->FindClosestTo( theFacetNormal.Location(), SMDSAbs_Volume );
2504
2505     delete searcher;
2506
2507     if ( !startHex )
2508       throw SALOME_Exception( THIS_METHOD "startHex not found");
2509   }
2510
2511   // Select a facet of startHex by theFacetNormal
2512
2513   SMDS_VolumeTool vTool( startHex );
2514   double norm[3], dot, maxDot = 0;
2515   int facetID = -1;
2516   for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2517     if ( vTool.GetFaceNormal( iF, norm[0], norm[1], norm[2] ))
2518     {
2519       dot = Abs( theFacetNormal.Direction().Dot( gp_Dir( norm[0], norm[1], norm[2] )));
2520       if ( dot > maxDot )
2521       {
2522         facetID = iF;
2523         maxDot = dot;
2524       }
2525     }
2526   if ( facetID < 0 )
2527     throw SALOME_Exception( THIS_METHOD "facet of startHex not found");
2528
2529   // Fill theFacets starting from facetID of startHex
2530
2531   // facets used for seach of volumes adjacent to already treated ones
2532   typedef pair< TFacetOfElem::iterator, int > TElemFacets;
2533   typedef map< TVolumeFaceKey, TElemFacets  > TFacetMap;
2534   TFacetMap facetsToCheck;
2535
2536   set<const SMDS_MeshNode*> facetNodes;
2537   const SMDS_MeshElement*   curHex;
2538
2539   const bool allHex = ((int) theHexas.size() == myMesh->NbHexas() );
2540
2541   while ( startHex )
2542   {
2543     // move in two directions from startHex via facetID
2544     for ( int is2nd = 0; is2nd < 2; ++is2nd )
2545     {
2546       curHex       = startHex;
2547       int curFacet = facetID;
2548       if ( is2nd ) // do not treat startHex twice
2549       {
2550         vTool.Set( curHex );
2551         if ( vTool.IsFreeFace( curFacet, &curHex ))
2552         {
2553           curHex = 0;
2554         }
2555         else
2556         {
2557           vTool.GetFaceNodes( curFacet, facetNodes );
2558           vTool.Set( curHex );
2559           curFacet = vTool.GetFaceIndex( facetNodes );
2560         }
2561       }
2562       while ( curHex )
2563       {
2564         // store a facet to split
2565         if ( curHex->GetGeomType() != SMDSGeom_HEXA )
2566         {
2567           theFacets.insert( make_pair( curHex, -1 ));
2568           break;
2569         }
2570         if ( !allHex && !theHexas.count( curHex ))
2571           break;
2572
2573         pair< TFacetOfElem::iterator, bool > facetIt2isNew =
2574           theFacets.insert( make_pair( curHex, curFacet ));
2575         if ( !facetIt2isNew.second )
2576           break;
2577
2578         // remember not-to-split facets in facetsToCheck
2579         int oppFacet = vTool.GetOppFaceIndexOfHex( curFacet );
2580         for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2581         {
2582           if ( iF == curFacet && iF == oppFacet )
2583             continue;
2584           TVolumeFaceKey facetKey ( vTool, iF );
2585           TElemFacets    elemFacet( facetIt2isNew.first, iF );
2586           pair< TFacetMap::iterator, bool > it2isnew =
2587             facetsToCheck.insert( make_pair( facetKey, elemFacet ));
2588           if ( !it2isnew.second )
2589             facetsToCheck.erase( it2isnew.first ); // adjacent hex already checked
2590         }
2591         // pass to a volume adjacent via oppFacet
2592         if ( vTool.IsFreeFace( oppFacet, &curHex ))
2593         {
2594           curHex = 0;
2595         }
2596         else
2597         {
2598           // get a new curFacet
2599           vTool.GetFaceNodes( oppFacet, facetNodes );
2600           vTool.Set( curHex );
2601           curFacet = vTool.GetFaceIndex( facetNodes, /*hint=*/curFacet );
2602         }
2603       }
2604     } // move in two directions from startHex via facetID
2605
2606     // Find a new startHex by facetsToCheck
2607
2608     startHex = 0;
2609     facetID  = -1;
2610     TFacetMap::iterator fIt = facetsToCheck.begin();
2611     while ( !startHex && fIt != facetsToCheck.end() )
2612     {
2613       const TElemFacets&  elemFacets = fIt->second;
2614       const SMDS_MeshElement*    hex = elemFacets.first->first;
2615       int                 splitFacet = elemFacets.first->second;
2616       int               lateralFacet = elemFacets.second;
2617       facetsToCheck.erase( fIt );
2618       fIt = facetsToCheck.begin();
2619
2620       vTool.Set( hex );
2621       if ( vTool.IsFreeFace( lateralFacet, &curHex ) || 
2622            curHex->GetGeomType() != SMDSGeom_HEXA )
2623         continue;
2624       if ( !allHex && !theHexas.count( curHex ))
2625         continue;
2626
2627       startHex = curHex;
2628
2629       // find a facet of startHex to split
2630
2631       set<const SMDS_MeshNode*> lateralNodes;
2632       vTool.GetFaceNodes( lateralFacet, lateralNodes );
2633       vTool.GetFaceNodes( splitFacet,   facetNodes );
2634       int oppLateralFacet = vTool.GetOppFaceIndexOfHex( lateralFacet );
2635       vTool.Set( startHex );
2636       lateralFacet = vTool.GetFaceIndex( lateralNodes, oppLateralFacet );
2637
2638       // look for a facet of startHex having common nodes with facetNodes
2639       // but not lateralFacet
2640       for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2641       {
2642         if ( iF == lateralFacet )
2643           continue;
2644         int nbCommonNodes = 0;
2645         const SMDS_MeshNode** nn = vTool.GetFaceNodes( iF );
2646         for ( int iN = 0, nbN = vTool.NbFaceNodes( iF ); iN < nbN; ++iN )
2647           nbCommonNodes += facetNodes.count( nn[ iN ]);
2648
2649         if ( nbCommonNodes >= 2 )
2650         {
2651           facetID = iF;
2652           break;
2653         }
2654       }
2655       if ( facetID < 0 )
2656         throw SALOME_Exception( THIS_METHOD "facet of a new startHex not found");
2657     }
2658   } //   while ( startHex )
2659
2660   return;
2661 }
2662
2663 namespace
2664 {
2665   //================================================================================
2666   /*!
2667    * \brief Selects nodes of several elements according to a given interlace
2668    *  \param [in] srcNodes - nodes to select from
2669    *  \param [out] tgtNodesVec - array of nodes of several elements to fill in
2670    *  \param [in] interlace - indices of nodes for all elements
2671    *  \param [in] nbElems - nb of elements
2672    *  \param [in] nbNodes - nb of nodes in each element
2673    *  \param [in] mesh - the mesh
2674    *  \param [out] elemQueue - a list to push elements found by the selected nodes
2675    *  \param [in] type - type of elements to look for
2676    */
2677   //================================================================================
2678
2679   void selectNodes( const vector< const SMDS_MeshNode* >& srcNodes,
2680                     vector< const SMDS_MeshNode* >*       tgtNodesVec,
2681                     const int*                            interlace,
2682                     const int                             nbElems,
2683                     const int                             nbNodes,
2684                     SMESHDS_Mesh*                         mesh = 0,
2685                     list< const SMDS_MeshElement* >*      elemQueue=0,
2686                     SMDSAbs_ElementType                   type=SMDSAbs_All)
2687   {
2688     for ( int iE = 0; iE < nbElems; ++iE )
2689     {
2690       vector< const SMDS_MeshNode* >& elemNodes = tgtNodesVec[iE];
2691       const int*                         select = & interlace[iE*nbNodes];
2692       elemNodes.resize( nbNodes );
2693       for ( int iN = 0; iN < nbNodes; ++iN )
2694         elemNodes[iN] = srcNodes[ select[ iN ]];
2695     }
2696     const SMDS_MeshElement* e;
2697     if ( elemQueue )
2698       for ( int iE = 0; iE < nbElems; ++iE )
2699         if (( e = mesh->FindElement( tgtNodesVec[iE], type, /*noMedium=*/false)))
2700           elemQueue->push_back( e );
2701   }
2702 }
2703
2704 //=======================================================================
2705 /*
2706  * Split bi-quadratic elements into linear ones without creation of additional nodes
2707  *   - bi-quadratic triangle will be split into 3 linear quadrangles;
2708  *   - bi-quadratic quadrangle will be split into 4 linear quadrangles;
2709  *   - tri-quadratic hexahedron will be split into 8 linear hexahedra;
2710  *   Quadratic elements of lower dimension  adjacent to the split bi-quadratic element
2711  *   will be split in order to keep the mesh conformal.
2712  *  \param elems - elements to split
2713  */
2714 //=======================================================================
2715
2716 void SMESH_MeshEditor::SplitBiQuadraticIntoLinear(TIDSortedElemSet& theElems)
2717 {
2718   vector< const SMDS_MeshNode* > elemNodes(27), subNodes[12], splitNodes[8];
2719   vector<const SMDS_MeshElement* > splitElems;
2720   list< const SMDS_MeshElement* > elemQueue;
2721   list< const SMDS_MeshElement* >::iterator elemIt;
2722
2723   SMESHDS_Mesh * mesh = GetMeshDS();
2724   ElemFeatures *elemType, hexaType(SMDSAbs_Volume), quadType(SMDSAbs_Face), segType(SMDSAbs_Edge);
2725   int nbElems, nbNodes;
2726
2727   TIDSortedElemSet::iterator elemSetIt = theElems.begin();
2728   for ( ; elemSetIt != theElems.end(); ++elemSetIt )
2729   {
2730     elemQueue.clear();
2731     elemQueue.push_back( *elemSetIt );
2732     for ( elemIt = elemQueue.begin(); elemIt != elemQueue.end(); ++elemIt )
2733     {
2734       const SMDS_MeshElement* elem = *elemIt;
2735       switch( elem->GetEntityType() )
2736       {
2737       case SMDSEntity_TriQuad_Hexa: // HEX27
2738       {
2739         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2740         nbElems  = nbNodes = 8;
2741         elemType = & hexaType;
2742
2743         // get nodes for new elements
2744         static int vInd[8][8] = {{ 0,8,20,11,   16,21,26,24 },
2745                                  { 1,9,20,8,    17,22,26,21 },
2746                                  { 2,10,20,9,   18,23,26,22 },
2747                                  { 3,11,20,10,  19,24,26,23 },
2748                                  { 16,21,26,24, 4,12,25,15  },
2749                                  { 17,22,26,21, 5,13,25,12  },
2750                                  { 18,23,26,22, 6,14,25,13  },
2751                                  { 19,24,26,23, 7,15,25,14  }};
2752         selectNodes( elemNodes, & splitNodes[0], &vInd[0][0], nbElems, nbNodes );
2753
2754         // add boundary faces to elemQueue
2755         static int fInd[6][9] = {{ 0,1,2,3, 8,9,10,11,   20 },
2756                                  { 4,5,6,7, 12,13,14,15, 25 },
2757                                  { 0,1,5,4, 8,17,12,16,  21 },
2758                                  { 1,2,6,5, 9,18,13,17,  22 },
2759                                  { 2,3,7,6, 10,19,14,18, 23 },
2760                                  { 3,0,4,7, 11,16,15,19, 24 }};
2761         selectNodes( elemNodes, & subNodes[0], &fInd[0][0], 6,9, mesh, &elemQueue, SMDSAbs_Face );
2762
2763         // add boundary segments to elemQueue
2764         static int eInd[12][3] = {{ 0,1,8 }, { 1,2,9 }, { 2,3,10 }, { 3,0,11 },
2765                                   { 4,5,12}, { 5,6,13}, { 6,7,14 }, { 7,4,15 },
2766                                   { 0,4,16}, { 1,5,17}, { 2,6,18 }, { 3,7,19 }};
2767         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 12,3, mesh, &elemQueue, SMDSAbs_Edge );
2768         break;
2769       }
2770       case SMDSEntity_BiQuad_Triangle: // TRIA7
2771       {
2772         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2773         nbElems = 3;
2774         nbNodes = 4;
2775         elemType = & quadType;
2776
2777         // get nodes for new elements
2778         static int fInd[3][4] = {{ 0,3,6,5 }, { 1,4,6,3 }, { 2,5,6,4 }};
2779         selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2780
2781         // add boundary segments to elemQueue
2782         static int eInd[3][3] = {{ 0,1,3 }, { 1,2,4 }, { 2,0,5 }};
2783         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 3,3, mesh, &elemQueue, SMDSAbs_Edge );
2784         break;
2785       }
2786       case SMDSEntity_BiQuad_Quadrangle: // QUAD9
2787       {
2788         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2789         nbElems = 4;
2790         nbNodes = 4;
2791         elemType = & quadType;
2792
2793         // get nodes for new elements
2794         static int fInd[4][4] = {{ 0,4,8,7 }, { 1,5,8,4 }, { 2,6,8,5 }, { 3,7,8,6 }};
2795         selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2796
2797         // add boundary segments to elemQueue
2798         static int eInd[4][3] = {{ 0,1,4 }, { 1,2,5 }, { 2,3,6 }, { 3,0,7 }};
2799         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 4,3, mesh, &elemQueue, SMDSAbs_Edge );
2800         break;
2801       }
2802       case SMDSEntity_Quad_Edge:
2803       {
2804         if ( elemIt == elemQueue.begin() )
2805           continue; // an elem is in theElems
2806         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2807         nbElems = 2;
2808         nbNodes = 2;
2809         elemType = & segType;
2810
2811         // get nodes for new elements
2812         static int eInd[2][2] = {{ 0,2 }, { 2,1 }};
2813         selectNodes( elemNodes, & splitNodes[0], &eInd[0][0], nbElems, nbNodes );
2814         break;
2815       }
2816       default: continue;
2817       } // switch( elem->GetEntityType() )
2818
2819       // Create new elements
2820
2821       SMESHDS_SubMesh* subMesh = mesh->MeshElements( elem->getshapeId() );
2822
2823       splitElems.clear();
2824
2825       //elemType->SetID( elem->GetID() ); // create an elem with the same ID as a removed one
2826       mesh->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2827       //splitElems.push_back( AddElement( splitNodes[ 0 ], *elemType ));
2828       //elemType->SetID( -1 );
2829
2830       for ( int iE = 0; iE < nbElems; ++iE )
2831         splitElems.push_back( AddElement( splitNodes[ iE ], *elemType ));
2832
2833
2834       ReplaceElemInGroups( elem, splitElems, mesh );
2835
2836       if ( subMesh )
2837         for ( size_t i = 0; i < splitElems.size(); ++i )
2838           subMesh->AddElement( splitElems[i] );
2839     }
2840   }
2841 }
2842
2843 //=======================================================================
2844 //function : AddToSameGroups
2845 //purpose  : add elemToAdd to the groups the elemInGroups belongs to
2846 //=======================================================================
2847
2848 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
2849                                         const SMDS_MeshElement* elemInGroups,
2850                                         SMESHDS_Mesh *          aMesh)
2851 {
2852   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2853   if (!groups.empty()) {
2854     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2855     for ( ; grIt != groups.end(); grIt++ ) {
2856       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2857       if ( group && group->Contains( elemInGroups ))
2858         group->SMDSGroup().Add( elemToAdd );
2859     }
2860   }
2861 }
2862
2863
2864 //=======================================================================
2865 //function : RemoveElemFromGroups
2866 //purpose  : Remove removeelem to the groups the elemInGroups belongs to
2867 //=======================================================================
2868 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2869                                              SMESHDS_Mesh *          aMesh)
2870 {
2871   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2872   if (!groups.empty())
2873   {
2874     set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2875     for (; GrIt != groups.end(); GrIt++)
2876     {
2877       SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2878       if (!grp || grp->IsEmpty()) continue;
2879       grp->SMDSGroup().Remove(removeelem);
2880     }
2881   }
2882 }
2883
2884 //================================================================================
2885 /*!
2886  * \brief Replace elemToRm by elemToAdd in the all groups
2887  */
2888 //================================================================================
2889
2890 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2891                                             const SMDS_MeshElement* elemToAdd,
2892                                             SMESHDS_Mesh *          aMesh)
2893 {
2894   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2895   if (!groups.empty()) {
2896     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2897     for ( ; grIt != groups.end(); grIt++ ) {
2898       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2899       if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2900         group->SMDSGroup().Add( elemToAdd );
2901     }
2902   }
2903 }
2904
2905 //================================================================================
2906 /*!
2907  * \brief Replace elemToRm by elemToAdd in the all groups
2908  */
2909 //================================================================================
2910
2911 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement*                elemToRm,
2912                                             const vector<const SMDS_MeshElement*>& elemToAdd,
2913                                             SMESHDS_Mesh *                         aMesh)
2914 {
2915   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2916   if (!groups.empty())
2917   {
2918     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2919     for ( ; grIt != groups.end(); grIt++ ) {
2920       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2921       if ( group && group->SMDSGroup().Remove( elemToRm ) )
2922         for ( size_t i = 0; i < elemToAdd.size(); ++i )
2923           group->SMDSGroup().Add( elemToAdd[ i ] );
2924     }
2925   }
2926 }
2927
2928 //=======================================================================
2929 //function : QuadToTri
2930 //purpose  : Cut quadrangles into triangles.
2931 //           theCrit is used to select a diagonal to cut
2932 //=======================================================================
2933
2934 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2935                                   const bool         the13Diag)
2936 {
2937   myLastCreatedElems.Clear();
2938   myLastCreatedNodes.Clear();
2939
2940   SMESHDS_Mesh * aMesh = GetMeshDS();
2941
2942   Handle(Geom_Surface) surface;
2943   SMESH_MesherHelper   helper( *GetMesh() );
2944
2945   TIDSortedElemSet::iterator itElem;
2946   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
2947   {
2948     const SMDS_MeshElement* elem = *itElem;
2949     if ( !elem || elem->GetGeomType() != SMDSGeom_QUADRANGLE )
2950       continue;
2951
2952     if ( elem->NbNodes() == 4 ) {
2953       // retrieve element nodes
2954       const SMDS_MeshNode* aNodes [4];
2955       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2956       int i = 0;
2957       while ( itN->more() )
2958         aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2959
2960       int aShapeId = FindShape( elem );
2961       const SMDS_MeshElement* newElem1 = 0;
2962       const SMDS_MeshElement* newElem2 = 0;
2963       if ( the13Diag ) {
2964         newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2965         newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2966       }
2967       else {
2968         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2969         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2970       }
2971       myLastCreatedElems.Append(newElem1);
2972       myLastCreatedElems.Append(newElem2);
2973       // put a new triangle on the same shape and add to the same groups
2974       if ( aShapeId )
2975       {
2976         aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2977         aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2978       }
2979       AddToSameGroups( newElem1, elem, aMesh );
2980       AddToSameGroups( newElem2, elem, aMesh );
2981       aMesh->RemoveElement( elem );
2982     }
2983
2984     // Quadratic quadrangle
2985
2986     else if ( elem->NbNodes() >= 8 )
2987     {
2988       // get surface elem is on
2989       int aShapeId = FindShape( elem );
2990       if ( aShapeId != helper.GetSubShapeID() ) {
2991         surface.Nullify();
2992         TopoDS_Shape shape;
2993         if ( aShapeId > 0 )
2994           shape = aMesh->IndexToShape( aShapeId );
2995         if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
2996           TopoDS_Face face = TopoDS::Face( shape );
2997           surface = BRep_Tool::Surface( face );
2998           if ( !surface.IsNull() )
2999             helper.SetSubShape( shape );
3000         }
3001       }
3002
3003       const SMDS_MeshNode* aNodes [9]; aNodes[8] = 0;
3004       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3005       for ( int i = 0; itN->more(); ++i )
3006         aNodes[ i ] = static_cast<const SMDS_MeshNode*>( itN->next() );
3007
3008       const SMDS_MeshNode* centrNode = aNodes[8];
3009       if ( centrNode == 0 )
3010       {
3011         centrNode = helper.GetCentralNode( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3012                                            aNodes[4], aNodes[5], aNodes[6], aNodes[7],
3013                                            surface.IsNull() );
3014         myLastCreatedNodes.Append(centrNode);
3015       }
3016
3017       // create a new element
3018       const SMDS_MeshElement* newElem1 = 0;
3019       const SMDS_MeshElement* newElem2 = 0;
3020       if ( the13Diag ) {
3021         newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
3022                                   aNodes[6], aNodes[7], centrNode );
3023         newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
3024                                   centrNode, aNodes[4], aNodes[5] );
3025       }
3026       else {
3027         newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
3028                                   aNodes[7], aNodes[4], centrNode );
3029         newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
3030                                   centrNode, aNodes[5], aNodes[6] );
3031       }
3032       myLastCreatedElems.Append(newElem1);
3033       myLastCreatedElems.Append(newElem2);
3034       // put a new triangle on the same shape and add to the same groups
3035       if ( aShapeId )
3036       {
3037         aMesh->SetMeshElementOnShape( newElem1, aShapeId );
3038         aMesh->SetMeshElementOnShape( newElem2, aShapeId );
3039       }
3040       AddToSameGroups( newElem1, elem, aMesh );
3041       AddToSameGroups( newElem2, elem, aMesh );
3042       aMesh->RemoveElement( elem );
3043     }
3044   }
3045
3046   return true;
3047 }
3048
3049 //=======================================================================
3050 //function : getAngle
3051 //purpose  :
3052 //=======================================================================
3053
3054 double getAngle(const SMDS_MeshElement * tr1,
3055                 const SMDS_MeshElement * tr2,
3056                 const SMDS_MeshNode *    n1,
3057                 const SMDS_MeshNode *    n2)
3058 {
3059   double angle = 2. * M_PI; // bad angle
3060
3061   // get normals
3062   SMESH::Controls::TSequenceOfXYZ P1, P2;
3063   if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
3064        !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
3065     return angle;
3066   gp_Vec N1,N2;
3067   if(!tr1->IsQuadratic())
3068     N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
3069   else
3070     N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
3071   if ( N1.SquareMagnitude() <= gp::Resolution() )
3072     return angle;
3073   if(!tr2->IsQuadratic())
3074     N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
3075   else
3076     N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
3077   if ( N2.SquareMagnitude() <= gp::Resolution() )
3078     return angle;
3079
3080   // find the first diagonal node n1 in the triangles:
3081   // take in account a diagonal link orientation
3082   const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
3083   for ( int t = 0; t < 2; t++ ) {
3084     SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
3085     int i = 0, iDiag = -1;
3086     while ( it->more()) {
3087       const SMDS_MeshElement *n = it->next();
3088       if ( n == n1 || n == n2 ) {
3089         if ( iDiag < 0)
3090           iDiag = i;
3091         else {
3092           if ( i - iDiag == 1 )
3093             nFirst[ t ] = ( n == n1 ? n2 : n1 );
3094           else
3095             nFirst[ t ] = n;
3096           break;
3097         }
3098       }
3099       i++;
3100     }
3101   }
3102   if ( nFirst[ 0 ] == nFirst[ 1 ] )
3103     N2.Reverse();
3104
3105   angle = N1.Angle( N2 );
3106   //SCRUTE( angle );
3107   return angle;
3108 }
3109
3110 // =================================================
3111 // class generating a unique ID for a pair of nodes
3112 // and able to return nodes by that ID
3113 // =================================================
3114 class LinkID_Gen {
3115 public:
3116
3117   LinkID_Gen( const SMESHDS_Mesh* theMesh )
3118     :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
3119   {}
3120
3121   long GetLinkID (const SMDS_MeshNode * n1,
3122                   const SMDS_MeshNode * n2) const
3123   {
3124     return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
3125   }
3126
3127   bool GetNodes (const long             theLinkID,
3128                  const SMDS_MeshNode* & theNode1,
3129                  const SMDS_MeshNode* & theNode2) const
3130   {
3131     theNode1 = myMesh->FindNode( theLinkID / myMaxID );
3132     if ( !theNode1 ) return false;
3133     theNode2 = myMesh->FindNode( theLinkID % myMaxID );
3134     if ( !theNode2 ) return false;
3135     return true;
3136   }
3137
3138 private:
3139   LinkID_Gen();
3140   const SMESHDS_Mesh* myMesh;
3141   long                myMaxID;
3142 };
3143
3144
3145 //=======================================================================
3146 //function : TriToQuad
3147 //purpose  : Fuse neighbour triangles into quadrangles.
3148 //           theCrit is used to select a neighbour to fuse with.
3149 //           theMaxAngle is a max angle between element normals at which
3150 //           fusion is still performed.
3151 //=======================================================================
3152
3153 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet &                   theElems,
3154                                   SMESH::Controls::NumericalFunctorPtr theCrit,
3155                                   const double                         theMaxAngle)
3156 {
3157   myLastCreatedElems.Clear();
3158   myLastCreatedNodes.Clear();
3159
3160   if ( !theCrit.get() )
3161     return false;
3162
3163   SMESHDS_Mesh * aMesh = GetMeshDS();
3164
3165   // Prepare data for algo: build
3166   // 1. map of elements with their linkIDs
3167   // 2. map of linkIDs with their elements
3168
3169   map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
3170   map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
3171   map< const SMDS_MeshElement*, set< SMESH_TLink > >  mapEl_setLi;
3172   map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
3173
3174   TIDSortedElemSet::iterator itElem;
3175   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
3176   {
3177     const SMDS_MeshElement* elem = *itElem;
3178     if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
3179     bool IsTria = ( elem->NbCornerNodes()==3 );
3180     if (!IsTria) continue;
3181
3182     // retrieve element nodes
3183     const SMDS_MeshNode* aNodes [4];
3184     SMDS_NodeIteratorPtr itN = elem->nodeIterator();
3185     int i = 0;
3186     while ( i < 3 )
3187       aNodes[ i++ ] = itN->next();
3188     aNodes[ 3 ] = aNodes[ 0 ];
3189
3190     // fill maps
3191     for ( i = 0; i < 3; i++ ) {
3192       SMESH_TLink link( aNodes[i], aNodes[i+1] );
3193       // check if elements sharing a link can be fused
3194       itLE = mapLi_listEl.find( link );
3195       if ( itLE != mapLi_listEl.end() ) {
3196         if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
3197           continue;
3198         const SMDS_MeshElement* elem2 = (*itLE).second.front();
3199         //if ( FindShape( elem ) != FindShape( elem2 ))
3200         //  continue; // do not fuse triangles laying on different shapes
3201         if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
3202           continue; // avoid making badly shaped quads
3203         (*itLE).second.push_back( elem );
3204       }
3205       else {
3206         mapLi_listEl[ link ].push_back( elem );
3207       }
3208       mapEl_setLi [ elem ].insert( link );
3209     }
3210   }
3211   // Clean the maps from the links shared by a sole element, ie
3212   // links to which only one element is bound in mapLi_listEl
3213
3214   for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
3215     int nbElems = (*itLE).second.size();
3216     if ( nbElems < 2  ) {
3217       const SMDS_MeshElement* elem = (*itLE).second.front();
3218       SMESH_TLink link = (*itLE).first;
3219       mapEl_setLi[ elem ].erase( link );
3220       if ( mapEl_setLi[ elem ].empty() )
3221         mapEl_setLi.erase( elem );
3222     }
3223   }
3224
3225   // Algo: fuse triangles into quadrangles
3226
3227   while ( ! mapEl_setLi.empty() ) {
3228     // Look for the start element:
3229     // the element having the least nb of shared links
3230     const SMDS_MeshElement* startElem = 0;
3231     int minNbLinks = 4;
3232     for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
3233       int nbLinks = (*itEL).second.size();
3234       if ( nbLinks < minNbLinks ) {
3235         startElem = (*itEL).first;
3236         minNbLinks = nbLinks;
3237         if ( minNbLinks == 1 )
3238           break;
3239       }
3240     }
3241
3242     // search elements to fuse starting from startElem or links of elements
3243     // fused earlyer - startLinks
3244     list< SMESH_TLink > startLinks;
3245     while ( startElem || !startLinks.empty() ) {
3246       while ( !startElem && !startLinks.empty() ) {
3247         // Get an element to start, by a link
3248         SMESH_TLink linkId = startLinks.front();
3249         startLinks.pop_front();
3250         itLE = mapLi_listEl.find( linkId );
3251         if ( itLE != mapLi_listEl.end() ) {
3252           list< const SMDS_MeshElement* > & listElem = (*itLE).second;
3253           list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
3254           for ( ; itE != listElem.end() ; itE++ )
3255             if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
3256               startElem = (*itE);
3257           mapLi_listEl.erase( itLE );
3258         }
3259       }
3260
3261       if ( startElem ) {
3262         // Get candidates to be fused
3263         const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
3264         const SMESH_TLink *link12 = 0, *link13 = 0;
3265         startElem = 0;
3266         ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
3267         set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
3268         ASSERT( !setLi.empty() );
3269         set< SMESH_TLink >::iterator itLi;
3270         for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
3271         {
3272           const SMESH_TLink & link = (*itLi);
3273           itLE = mapLi_listEl.find( link );
3274           if ( itLE == mapLi_listEl.end() )
3275             continue;
3276
3277           const SMDS_MeshElement* elem = (*itLE).second.front();
3278           if ( elem == tr1 )
3279             elem = (*itLE).second.back();
3280           mapLi_listEl.erase( itLE );
3281           if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
3282             continue;
3283           if ( tr2 ) {
3284             tr3 = elem;
3285             link13 = &link;
3286           }
3287           else {
3288             tr2 = elem;
3289             link12 = &link;
3290           }
3291
3292           // add other links of elem to list of links to re-start from
3293           set< SMESH_TLink >& links = mapEl_setLi[ elem ];
3294           set< SMESH_TLink >::iterator it;
3295           for ( it = links.begin(); it != links.end(); it++ ) {
3296             const SMESH_TLink& link2 = (*it);
3297             if ( link2 != link )
3298               startLinks.push_back( link2 );
3299           }
3300         }
3301
3302         // Get nodes of possible quadrangles
3303         const SMDS_MeshNode *n12 [4], *n13 [4];
3304         bool Ok12 = false, Ok13 = false;
3305         const SMDS_MeshNode *linkNode1, *linkNode2;
3306         if(tr2) {
3307           linkNode1 = link12->first;
3308           linkNode2 = link12->second;
3309           if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
3310             Ok12 = true;
3311         }
3312         if(tr3) {
3313           linkNode1 = link13->first;
3314           linkNode2 = link13->second;
3315           if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
3316             Ok13 = true;
3317         }
3318
3319         // Choose a pair to fuse
3320         if ( Ok12 && Ok13 ) {
3321           SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
3322           SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
3323           double aBadRate12 = getBadRate( &quad12, theCrit );
3324           double aBadRate13 = getBadRate( &quad13, theCrit );
3325           if (  aBadRate13 < aBadRate12 )
3326             Ok12 = false;
3327           else
3328             Ok13 = false;
3329         }
3330
3331         // Make quadrangles
3332         // and remove fused elems and remove links from the maps
3333         mapEl_setLi.erase( tr1 );
3334         if ( Ok12 )
3335         {
3336           mapEl_setLi.erase( tr2 );
3337           mapLi_listEl.erase( *link12 );
3338           if ( tr1->NbNodes() == 3 )
3339           {
3340             const SMDS_MeshElement* newElem = 0;
3341             newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
3342             myLastCreatedElems.Append(newElem);
3343             AddToSameGroups( newElem, tr1, aMesh );
3344             int aShapeId = tr1->getshapeId();
3345             if ( aShapeId )
3346               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3347             aMesh->RemoveElement( tr1 );
3348             aMesh->RemoveElement( tr2 );
3349           }
3350           else {
3351             vector< const SMDS_MeshNode* > N1;
3352             vector< const SMDS_MeshNode* > N2;
3353             getNodesFromTwoTria(tr1,tr2,N1,N2);
3354             // now we receive following N1 and N2 (using numeration as in image in InverseDiag())
3355             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
3356             // i.e. first nodes from both arrays form a new diagonal
3357             const SMDS_MeshNode* aNodes[8];
3358             aNodes[0] = N1[0];
3359             aNodes[1] = N1[1];
3360             aNodes[2] = N2[0];
3361             aNodes[3] = N2[1];
3362             aNodes[4] = N1[3];
3363             aNodes[5] = N2[5];
3364             aNodes[6] = N2[3];
3365             aNodes[7] = N1[5];
3366             const SMDS_MeshElement* newElem = 0;
3367             if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3368               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3369                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3370             else
3371               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3372                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3373             myLastCreatedElems.Append(newElem);
3374             AddToSameGroups( newElem, tr1, aMesh );
3375             int aShapeId = tr1->getshapeId();
3376             if ( aShapeId )
3377               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3378             aMesh->RemoveElement( tr1 );
3379             aMesh->RemoveElement( tr2 );
3380             // remove middle node (9)
3381             if ( N1[4]->NbInverseElements() == 0 )
3382               aMesh->RemoveNode( N1[4] );
3383             if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3384               aMesh->RemoveNode( N1[6] );
3385             if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3386               aMesh->RemoveNode( N2[6] );
3387           }
3388         }
3389         else if ( Ok13 )
3390         {
3391           mapEl_setLi.erase( tr3 );
3392           mapLi_listEl.erase( *link13 );
3393           if ( tr1->NbNodes() == 3 ) {
3394             const SMDS_MeshElement* newElem = 0;
3395             newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
3396             myLastCreatedElems.Append(newElem);
3397             AddToSameGroups( newElem, tr1, aMesh );
3398             int aShapeId = tr1->getshapeId();
3399             if ( aShapeId )
3400               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3401             aMesh->RemoveElement( tr1 );
3402             aMesh->RemoveElement( tr3 );
3403           }
3404           else {
3405             vector< const SMDS_MeshNode* > N1;
3406             vector< const SMDS_MeshNode* > N2;
3407             getNodesFromTwoTria(tr1,tr3,N1,N2);
3408             // now we receive following N1 and N2 (using numeration as above image)
3409             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
3410             // i.e. first nodes from both arrays form a new diagonal
3411             const SMDS_MeshNode* aNodes[8];
3412             aNodes[0] = N1[0];
3413             aNodes[1] = N1[1];
3414             aNodes[2] = N2[0];
3415             aNodes[3] = N2[1];
3416             aNodes[4] = N1[3];
3417             aNodes[5] = N2[5];
3418             aNodes[6] = N2[3];
3419             aNodes[7] = N1[5];
3420             const SMDS_MeshElement* newElem = 0;
3421             if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3422               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3423                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3424             else
3425               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3426                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3427             myLastCreatedElems.Append(newElem);
3428             AddToSameGroups( newElem, tr1, aMesh );
3429             int aShapeId = tr1->getshapeId();
3430             if ( aShapeId )
3431               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3432             aMesh->RemoveElement( tr1 );
3433             aMesh->RemoveElement( tr3 );
3434             // remove middle node (9)
3435             if ( N1[4]->NbInverseElements() == 0 )
3436               aMesh->RemoveNode( N1[4] );
3437             if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3438               aMesh->RemoveNode( N1[6] );
3439             if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3440               aMesh->RemoveNode( N2[6] );
3441           }
3442         }
3443
3444         // Next element to fuse: the rejected one
3445         if ( tr3 )
3446           startElem = Ok12 ? tr3 : tr2;
3447
3448       } // if ( startElem )
3449     } // while ( startElem || !startLinks.empty() )
3450   } // while ( ! mapEl_setLi.empty() )
3451
3452   return true;
3453 }
3454
3455
3456 /*#define DUMPSO(txt) \
3457 //  cout << txt << endl;
3458 //=============================================================================
3459 //
3460 //
3461 //
3462 //=============================================================================
3463 static void swap( int i1, int i2, int idNodes[], gp_Pnt P[] )
3464 {
3465 if ( i1 == i2 )
3466 return;
3467 int tmp = idNodes[ i1 ];
3468 idNodes[ i1 ] = idNodes[ i2 ];
3469 idNodes[ i2 ] = tmp;
3470 gp_Pnt Ptmp = P[ i1 ];
3471 P[ i1 ] = P[ i2 ];
3472 P[ i2 ] = Ptmp;
3473 DUMPSO( i1 << "(" << idNodes[ i2 ] << ") <-> " << i2 << "(" << idNodes[ i1 ] << ")");
3474 }
3475
3476 //=======================================================================
3477 //function : SortQuadNodes
3478 //purpose  : Set 4 nodes of a quadrangle face in a good order.
3479 //           Swap 1<->2 or 2<->3 nodes and correspondingly return
3480 //           1 or 2 else 0.
3481 //=======================================================================
3482
3483 int SMESH_MeshEditor::SortQuadNodes (const SMDS_Mesh * theMesh,
3484 int               idNodes[] )
3485 {
3486   gp_Pnt P[4];
3487   int i;
3488   for ( i = 0; i < 4; i++ ) {
3489     const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
3490     if ( !n ) return 0;
3491     P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
3492   }
3493
3494   gp_Vec V1(P[0], P[1]);
3495   gp_Vec V2(P[0], P[2]);
3496   gp_Vec V3(P[0], P[3]);
3497
3498   gp_Vec Cross1 = V1 ^ V2;
3499   gp_Vec Cross2 = V2 ^ V3;
3500
3501   i = 0;
3502   if (Cross1.Dot(Cross2) < 0)
3503   {
3504     Cross1 = V2 ^ V1;
3505     Cross2 = V1 ^ V3;
3506
3507     if (Cross1.Dot(Cross2) < 0)
3508       i = 2;
3509     else
3510       i = 1;
3511     swap ( i, i + 1, idNodes, P );
3512
3513     //     for ( int ii = 0; ii < 4; ii++ ) {
3514     //       const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
3515     //       DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3516     //     }
3517   }
3518   return i;
3519 }
3520
3521 //=======================================================================
3522 //function : SortHexaNodes
3523 //purpose  : Set 8 nodes of a hexahedron in a good order.
3524 //           Return success status
3525 //=======================================================================
3526
3527 bool SMESH_MeshEditor::SortHexaNodes (const SMDS_Mesh * theMesh,
3528                                       int               idNodes[] )
3529 {
3530   gp_Pnt P[8];
3531   int i;
3532   DUMPSO( "INPUT: ========================================");
3533   for ( i = 0; i < 8; i++ ) {
3534     const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
3535     if ( !n ) return false;
3536     P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
3537     DUMPSO( i << "(" << idNodes[i] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3538   }
3539   DUMPSO( "========================================");
3540
3541
3542   set<int> faceNodes;  // ids of bottom face nodes, to be found
3543   set<int> checkedId1; // ids of tried 2-nd nodes
3544   Standard_Real leastDist = DBL_MAX; // dist of the 4-th node from 123 plane
3545   const Standard_Real tol = 1.e-6;   // tolerance to find nodes in plane
3546   int iMin, iLoop1 = 0;
3547
3548   // Loop to try the 2-nd nodes
3549
3550   while ( leastDist > DBL_MIN && ++iLoop1 < 8 )
3551   {
3552     // Find not checked 2-nd node
3553     for ( i = 1; i < 8; i++ )
3554       if ( checkedId1.find( idNodes[i] ) == checkedId1.end() ) {
3555         int id1 = idNodes[i];
3556         swap ( 1, i, idNodes, P );
3557         checkedId1.insert ( id1 );
3558         break;
3559       }
3560
3561     // Find the 3-d node so that 1-2-3 triangle to be on a hexa face,
3562     // ie that all but meybe one (id3 which is on the same face) nodes
3563     // lay on the same side from the triangle plane.
3564
3565     bool manyInPlane = false; // more than 4 nodes lay in plane
3566     int iLoop2 = 0;
3567     while ( ++iLoop2 < 6 ) {
3568
3569       // get 1-2-3 plane coeffs
3570       Standard_Real A, B, C, D;
3571       gp_Vec N = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
3572       if ( N.SquareMagnitude() > gp::Resolution() )
3573       {
3574         gp_Pln pln ( P[0], N );
3575         pln.Coefficients( A, B, C, D );
3576
3577         // find the node (iMin) closest to pln
3578         Standard_Real dist[ 8 ], minDist = DBL_MAX;
3579         set<int> idInPln;
3580         for ( i = 3; i < 8; i++ ) {
3581           dist[i] = A * P[i].X() + B * P[i].Y() + C * P[i].Z() + D;
3582           if ( fabs( dist[i] ) < minDist ) {
3583             minDist = fabs( dist[i] );
3584             iMin = i;
3585           }
3586           if ( fabs( dist[i] ) <= tol )
3587             idInPln.insert( idNodes[i] );
3588         }
3589
3590         // there should not be more than 4 nodes in bottom plane
3591         if ( idInPln.size() > 1 )
3592         {
3593           DUMPSO( "### idInPln.size() = " << idInPln.size());
3594           // idInPlane does not contain the first 3 nodes
3595           if ( manyInPlane || idInPln.size() == 5)
3596             return false; // all nodes in one plane
3597           manyInPlane = true;
3598
3599           // set the 1-st node to be not in plane
3600           for ( i = 3; i < 8; i++ ) {
3601             if ( idInPln.find( idNodes[ i ] ) == idInPln.end() ) {
3602               DUMPSO( "### Reset 0-th node");
3603               swap( 0, i, idNodes, P );
3604               break;
3605             }
3606           }
3607
3608           // reset to re-check second nodes
3609           leastDist = DBL_MAX;
3610           faceNodes.clear();
3611           checkedId1.clear();
3612           iLoop1 = 0;
3613           break; // from iLoop2;
3614         }
3615
3616         // check that the other 4 nodes are on the same side
3617         bool sameSide = true;
3618         bool isNeg = dist[ iMin == 3 ? 4 : 3 ] <= 0.;
3619         for ( i = 3; sameSide && i < 8; i++ ) {
3620           if ( i != iMin )
3621             sameSide = ( isNeg == dist[i] <= 0.);
3622         }
3623
3624         // keep best solution
3625         if ( sameSide && minDist < leastDist ) {
3626           leastDist = minDist;
3627           faceNodes.clear();
3628           faceNodes.insert( idNodes[ 1 ] );
3629           faceNodes.insert( idNodes[ 2 ] );
3630           faceNodes.insert( idNodes[ iMin ] );
3631           DUMPSO( "loop " << iLoop2 << " id2 " << idNodes[ 1 ] << " id3 " << idNodes[ 2 ]
3632                   << " leastDist = " << leastDist);
3633           if ( leastDist <= DBL_MIN )
3634             break;
3635         }
3636       }
3637
3638       // set next 3-d node to check
3639       int iNext = 2 + iLoop2;
3640       if ( iNext < 8 ) {
3641         DUMPSO( "Try 2-nd");
3642         swap ( 2, iNext, idNodes, P );
3643       }
3644     } // while ( iLoop2 < 6 )
3645   } // iLoop1
3646
3647   if ( faceNodes.empty() ) return false;
3648
3649   // Put the faceNodes in proper places
3650   for ( i = 4; i < 8; i++ ) {
3651     if ( faceNodes.find( idNodes[ i ] ) != faceNodes.end() ) {
3652       // find a place to put
3653       int iTo = 1;
3654       while ( faceNodes.find( idNodes[ iTo ] ) != faceNodes.end() )
3655         iTo++;
3656       DUMPSO( "Set faceNodes");
3657       swap ( iTo, i, idNodes, P );
3658     }
3659   }
3660
3661
3662   // Set nodes of the found bottom face in good order
3663   DUMPSO( " Found bottom face: ");
3664   i = SortQuadNodes( theMesh, idNodes );
3665   if ( i ) {
3666     gp_Pnt Ptmp = P[ i ];
3667     P[ i ] = P[ i+1 ];
3668     P[ i+1 ] = Ptmp;
3669   }
3670   //   else
3671   //     for ( int ii = 0; ii < 4; ii++ ) {
3672   //       const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
3673   //       DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3674   //    }
3675
3676   // Gravity center of the top and bottom faces
3677   gp_Pnt aGCb = ( P[0].XYZ() + P[1].XYZ() + P[2].XYZ() + P[3].XYZ() ) / 4.;
3678   gp_Pnt aGCt = ( P[4].XYZ() + P[5].XYZ() + P[6].XYZ() + P[7].XYZ() ) / 4.;
3679
3680   // Get direction from the bottom to the top face
3681   gp_Vec upDir ( aGCb, aGCt );
3682   Standard_Real upDirSize = upDir.Magnitude();
3683   if ( upDirSize <= gp::Resolution() ) return false;
3684   upDir / upDirSize;
3685
3686   // Assure that the bottom face normal points up
3687   gp_Vec Nb = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
3688   Nb += gp_Vec (P[0], P[2]).Crossed( gp_Vec (P[0], P[3]) );
3689   if ( Nb.Dot( upDir ) < 0 ) {
3690     DUMPSO( "Reverse bottom face");
3691     swap( 1, 3, idNodes, P );
3692   }
3693
3694   // Find 5-th node - the one closest to the 1-st among the last 4 nodes.
3695   Standard_Real minDist = DBL_MAX;
3696   for ( i = 4; i < 8; i++ ) {
3697     // projection of P[i] to the plane defined by P[0] and upDir
3698     gp_Pnt Pp = P[i].Translated( upDir * ( upDir.Dot( gp_Vec( P[i], P[0] ))));
3699     Standard_Real sqDist = P[0].SquareDistance( Pp );
3700     if ( sqDist < minDist ) {
3701       minDist = sqDist;
3702       iMin = i;
3703     }
3704   }
3705   DUMPSO( "Set 4-th");
3706   swap ( 4, iMin, idNodes, P );
3707
3708   // Set nodes of the top face in good order
3709   DUMPSO( "Sort top face");
3710   i = SortQuadNodes( theMesh, &idNodes[4] );
3711   if ( i ) {
3712     i += 4;
3713     gp_Pnt Ptmp = P[ i ];
3714     P[ i ] = P[ i+1 ];
3715     P[ i+1 ] = Ptmp;
3716   }
3717
3718   // Assure that direction of the top face normal is from the bottom face
3719   gp_Vec Nt = gp_Vec (P[4], P[5]).Crossed( gp_Vec (P[4], P[6]) );
3720   Nt += gp_Vec (P[4], P[6]).Crossed( gp_Vec (P[4], P[7]) );
3721   if ( Nt.Dot( upDir ) < 0 ) {
3722     DUMPSO( "Reverse top face");
3723     swap( 5, 7, idNodes, P );
3724   }
3725
3726   //   DUMPSO( "OUTPUT: ========================================");
3727   //   for ( i = 0; i < 8; i++ ) {
3728   //     float *p = ugrid->GetPoint(idNodes[i]);
3729   //     DUMPSO( i << "(" << idNodes[i] << ") : " << p[0] << " " << p[1] << " " << p[2]);
3730   //   }
3731
3732   return true;
3733 }*/
3734
3735 //================================================================================
3736 /*!
3737  * \brief Return nodes linked to the given one
3738  * \param theNode - the node
3739  * \param linkedNodes - the found nodes
3740  * \param type - the type of elements to check
3741  *
3742  * Medium nodes are ignored
3743  */
3744 //================================================================================
3745
3746 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
3747                                        TIDSortedElemSet &   linkedNodes,
3748                                        SMDSAbs_ElementType  type )
3749 {
3750   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
3751   while ( elemIt->more() )
3752   {
3753     const SMDS_MeshElement* elem = elemIt->next();
3754     if(elem->GetType() == SMDSAbs_0DElement)
3755       continue;
3756
3757     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
3758     if ( elem->GetType() == SMDSAbs_Volume )
3759     {
3760       SMDS_VolumeTool vol( elem );
3761       while ( nodeIt->more() ) {
3762         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3763         if ( theNode != n && vol.IsLinked( theNode, n ))
3764           linkedNodes.insert( n );
3765       }
3766     }
3767     else
3768     {
3769       for ( int i = 0; nodeIt->more(); ++i ) {
3770         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3771         if ( n == theNode ) {
3772           int iBefore = i - 1;
3773           int iAfter  = i + 1;
3774           if ( elem->IsQuadratic() ) {
3775             int nb = elem->NbNodes() / 2;
3776             iAfter  = SMESH_MesherHelper::WrapIndex( iAfter, nb );
3777             iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
3778           }
3779           linkedNodes.insert( elem->GetNodeWrap( iAfter ));
3780           linkedNodes.insert( elem->GetNodeWrap( iBefore ));
3781         }
3782       }
3783     }
3784   }
3785 }
3786
3787 //=======================================================================
3788 //function : laplacianSmooth
3789 //purpose  : pulls theNode toward the center of surrounding nodes directly
3790 //           connected to that node along an element edge
3791 //=======================================================================
3792
3793 void laplacianSmooth(const SMDS_MeshNode*                 theNode,
3794                      const Handle(Geom_Surface)&          theSurface,
3795                      map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3796 {
3797   // find surrounding nodes
3798
3799   TIDSortedElemSet nodeSet;
3800   SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
3801
3802   // compute new coodrs
3803
3804   double coord[] = { 0., 0., 0. };
3805   TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3806   for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
3807     const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3808     if ( theSurface.IsNull() ) { // smooth in 3D
3809       coord[0] += node->X();
3810       coord[1] += node->Y();
3811       coord[2] += node->Z();
3812     }
3813     else { // smooth in 2D
3814       ASSERT( theUVMap.find( node ) != theUVMap.end() );
3815       gp_XY* uv = theUVMap[ node ];
3816       coord[0] += uv->X();
3817       coord[1] += uv->Y();
3818     }
3819   }
3820   int nbNodes = nodeSet.size();
3821   if ( !nbNodes )
3822     return;
3823   coord[0] /= nbNodes;
3824   coord[1] /= nbNodes;
3825
3826   if ( !theSurface.IsNull() ) {
3827     ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3828     theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3829     gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3830     coord[0] = p3d.X();
3831     coord[1] = p3d.Y();
3832     coord[2] = p3d.Z();
3833   }
3834   else
3835     coord[2] /= nbNodes;
3836
3837   // move node
3838
3839   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3840 }
3841
3842 //=======================================================================
3843 //function : centroidalSmooth
3844 //purpose  : pulls theNode toward the element-area-weighted centroid of the
3845 //           surrounding elements
3846 //=======================================================================
3847
3848 void centroidalSmooth(const SMDS_MeshNode*                 theNode,
3849                       const Handle(Geom_Surface)&          theSurface,
3850                       map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3851 {
3852   gp_XYZ aNewXYZ(0.,0.,0.);
3853   SMESH::Controls::Area anAreaFunc;
3854   double totalArea = 0.;
3855   int nbElems = 0;
3856
3857   // compute new XYZ
3858
3859   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3860   while ( elemIt->more() )
3861   {
3862     const SMDS_MeshElement* elem = elemIt->next();
3863     nbElems++;
3864
3865     gp_XYZ elemCenter(0.,0.,0.);
3866     SMESH::Controls::TSequenceOfXYZ aNodePoints;
3867     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3868     int nn = elem->NbNodes();
3869     if(elem->IsQuadratic()) nn = nn/2;
3870     int i=0;
3871     //while ( itN->more() ) {
3872     while ( i<nn ) {
3873       const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3874       i++;
3875       gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3876       aNodePoints.push_back( aP );
3877       if ( !theSurface.IsNull() ) { // smooth in 2D
3878         ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3879         gp_XY* uv = theUVMap[ aNode ];
3880         aP.SetCoord( uv->X(), uv->Y(), 0. );
3881       }
3882       elemCenter += aP;
3883     }
3884     double elemArea = anAreaFunc.GetValue( aNodePoints );
3885     totalArea += elemArea;
3886     elemCenter /= nn;
3887     aNewXYZ += elemCenter * elemArea;
3888   }
3889   aNewXYZ /= totalArea;
3890   if ( !theSurface.IsNull() ) {
3891     theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3892     aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3893   }
3894
3895   // move node
3896
3897   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3898 }
3899
3900 //=======================================================================
3901 //function : getClosestUV
3902 //purpose  : return UV of closest projection
3903 //=======================================================================
3904
3905 static bool getClosestUV (Extrema_GenExtPS& projector,
3906                           const gp_Pnt&     point,
3907                           gp_XY &           result)
3908 {
3909   projector.Perform( point );
3910   if ( projector.IsDone() ) {
3911     double u, v, minVal = DBL_MAX;
3912     for ( int i = projector.NbExt(); i > 0; i-- )
3913       if ( projector.SquareDistance( i ) < minVal ) {
3914         minVal = projector.SquareDistance( i );
3915         projector.Point( i ).Parameter( u, v );
3916       }
3917     result.SetCoord( u, v );
3918     return true;
3919   }
3920   return false;
3921 }
3922
3923 //=======================================================================
3924 //function : Smooth
3925 //purpose  : Smooth theElements during theNbIterations or until a worst
3926 //           element has aspect ratio <= theTgtAspectRatio.
3927 //           Aspect Ratio varies in range [1.0, inf].
3928 //           If theElements is empty, the whole mesh is smoothed.
3929 //           theFixedNodes contains additionally fixed nodes. Nodes built
3930 //           on edges and boundary nodes are always fixed.
3931 //=======================================================================
3932
3933 void SMESH_MeshEditor::Smooth (TIDSortedElemSet &          theElems,
3934                                set<const SMDS_MeshNode*> & theFixedNodes,
3935                                const SmoothMethod          theSmoothMethod,
3936                                const int                   theNbIterations,
3937                                double                      theTgtAspectRatio,
3938                                const bool                  the2D)
3939 {
3940   myLastCreatedElems.Clear();
3941   myLastCreatedNodes.Clear();
3942
3943   if ( theTgtAspectRatio < 1.0 )
3944     theTgtAspectRatio = 1.0;
3945
3946   const double disttol = 1.e-16;
3947
3948   SMESH::Controls::AspectRatio aQualityFunc;
3949
3950   SMESHDS_Mesh* aMesh = GetMeshDS();
3951
3952   if ( theElems.empty() ) {
3953     // add all faces to theElems
3954     SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3955     while ( fIt->more() ) {
3956       const SMDS_MeshElement* face = fIt->next();
3957       theElems.insert( theElems.end(), face );
3958     }
3959   }
3960   // get all face ids theElems are on
3961   set< int > faceIdSet;
3962   TIDSortedElemSet::iterator itElem;
3963   if ( the2D )
3964     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3965       int fId = FindShape( *itElem );
3966       // check that corresponding submesh exists and a shape is face
3967       if (fId &&
3968           faceIdSet.find( fId ) == faceIdSet.end() &&
3969           aMesh->MeshElements( fId )) {
3970         TopoDS_Shape F = aMesh->IndexToShape( fId );
3971         if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3972           faceIdSet.insert( fId );
3973       }
3974     }
3975   faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3976
3977   // ===============================================
3978   // smooth elements on each TopoDS_Face separately
3979   // ===============================================
3980
3981   SMESH_MesherHelper helper( *GetMesh() );
3982
3983   set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treat 0 fId at the end
3984   for ( ; fId != faceIdSet.rend(); ++fId )
3985   {
3986     // get face surface and submesh
3987     Handle(Geom_Surface) surface;
3988     SMESHDS_SubMesh* faceSubMesh = 0;
3989     TopoDS_Face face;
3990     double fToler2 = 0;
3991     double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3992     bool isUPeriodic = false, isVPeriodic = false;
3993     if ( *fId )
3994     {
3995       face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3996       surface = BRep_Tool::Surface( face );
3997       faceSubMesh = aMesh->MeshElements( *fId );
3998       fToler2 = BRep_Tool::Tolerance( face );
3999       fToler2 *= fToler2 * 10.;
4000       isUPeriodic = surface->IsUPeriodic();
4001       if ( isUPeriodic )
4002         surface->UPeriod();
4003       isVPeriodic = surface->IsVPeriodic();
4004       if ( isVPeriodic )
4005         surface->VPeriod();
4006       surface->Bounds( u1, u2, v1, v2 );
4007       helper.SetSubShape( face );
4008     }
4009     // ---------------------------------------------------------
4010     // for elements on a face, find movable and fixed nodes and
4011     // compute UV for them
4012     // ---------------------------------------------------------
4013     bool checkBoundaryNodes = false;
4014     bool isQuadratic = false;
4015     set<const SMDS_MeshNode*> setMovableNodes;
4016     map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
4017     list< gp_XY > listUV; // uvs the 2 uvMaps refer to
4018     list< const SMDS_MeshElement* > elemsOnFace;
4019
4020     Extrema_GenExtPS projector;
4021     GeomAdaptor_Surface surfAdaptor;
4022     if ( !surface.IsNull() ) {
4023       surfAdaptor.Load( surface );
4024       projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
4025     }
4026     int nbElemOnFace = 0;
4027     itElem = theElems.begin();
4028     // loop on not yet smoothed elements: look for elems on a face
4029     while ( itElem != theElems.end() )
4030     {
4031       if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
4032         break; // all elements found
4033
4034       const SMDS_MeshElement* elem = *itElem;
4035       if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
4036            ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
4037         ++itElem;
4038         continue;
4039       }
4040       elemsOnFace.push_back( elem );
4041       theElems.erase( itElem++ );
4042       nbElemOnFace++;
4043
4044       if ( !isQuadratic )
4045         isQuadratic = elem->IsQuadratic();
4046
4047       // get movable nodes of elem
4048       const SMDS_MeshNode* node;
4049       SMDS_TypeOfPosition posType;
4050       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4051       int nn = 0, nbn =  elem->NbNodes();
4052       if(elem->IsQuadratic())
4053         nbn = nbn/2;
4054       while ( nn++ < nbn ) {
4055         node = static_cast<const SMDS_MeshNode*>( itN->next() );
4056         const SMDS_PositionPtr& pos = node->GetPosition();
4057         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
4058         if (posType != SMDS_TOP_EDGE &&
4059             posType != SMDS_TOP_VERTEX &&
4060             theFixedNodes.find( node ) == theFixedNodes.end())
4061         {
4062           // check if all faces around the node are on faceSubMesh
4063           // because a node on edge may be bound to face
4064           SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
4065           bool all = true;
4066           if ( faceSubMesh ) {
4067             while ( eIt->more() && all ) {
4068               const SMDS_MeshElement* e = eIt->next();
4069               all = faceSubMesh->Contains( e );
4070             }
4071           }
4072           if ( all )
4073             setMovableNodes.insert( node );
4074           else
4075             checkBoundaryNodes = true;
4076         }
4077         if ( posType == SMDS_TOP_3DSPACE )
4078           checkBoundaryNodes = true;
4079       }
4080
4081       if ( surface.IsNull() )
4082         continue;
4083
4084       // get nodes to check UV
4085       list< const SMDS_MeshNode* > uvCheckNodes;
4086       const SMDS_MeshNode* nodeInFace = 0;
4087       itN = elem->nodesIterator();
4088       nn = 0; nbn =  elem->NbNodes();
4089       if(elem->IsQuadratic())
4090         nbn = nbn/2;
4091       while ( nn++ < nbn ) {
4092         node = static_cast<const SMDS_MeshNode*>( itN->next() );
4093         if ( node->GetPosition()->GetDim() == 2 )
4094           nodeInFace = node;
4095         if ( uvMap.find( node ) == uvMap.end() )
4096           uvCheckNodes.push_back( node );
4097         // add nodes of elems sharing node
4098         //         SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
4099         //         while ( eIt->more() ) {
4100         //           const SMDS_MeshElement* e = eIt->next();
4101         //           if ( e != elem ) {
4102         //             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4103         //             while ( nIt->more() ) {
4104         //               const SMDS_MeshNode* n =
4105         //                 static_cast<const SMDS_MeshNode*>( nIt->next() );
4106         //               if ( uvMap.find( n ) == uvMap.end() )
4107         //                 uvCheckNodes.push_back( n );
4108         //             }
4109         //           }
4110         //         }
4111       }
4112       // check UV on face
4113       list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
4114       for ( ; n != uvCheckNodes.end(); ++n ) {
4115         node = *n;
4116         gp_XY uv( 0, 0 );
4117         const SMDS_PositionPtr& pos = node->GetPosition();
4118         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
4119         // get existing UV
4120         if ( pos )
4121         {
4122           bool toCheck = true;
4123           uv = helper.GetNodeUV( face, node, nodeInFace, &toCheck );
4124         }
4125         // compute not existing UV
4126         bool project = ( posType == SMDS_TOP_3DSPACE );
4127         // double dist1 = DBL_MAX, dist2 = 0;
4128         // if ( posType != SMDS_TOP_3DSPACE ) {
4129         //   dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
4130         //   project = dist1 > fToler2;
4131         // }
4132         if ( project ) { // compute new UV
4133           gp_XY newUV;
4134           gp_Pnt pNode = SMESH_TNodeXYZ( node );
4135           if ( !getClosestUV( projector, pNode, newUV )) {
4136             MESSAGE("Node Projection Failed " << node);
4137           }
4138           else {
4139             if ( isUPeriodic )
4140               newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
4141             if ( isVPeriodic )
4142               newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
4143             // check new UV
4144             // if ( posType != SMDS_TOP_3DSPACE )
4145             //   dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
4146             // if ( dist2 < dist1 )
4147               uv = newUV;
4148           }
4149         }
4150         // store UV in the map
4151         listUV.push_back( uv );
4152         uvMap.insert( make_pair( node, &listUV.back() ));
4153       }
4154     } // loop on not yet smoothed elements
4155
4156     if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
4157       checkBoundaryNodes = true;
4158
4159     // fix nodes on mesh boundary
4160
4161     if ( checkBoundaryNodes ) {
4162       map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
4163       map< SMESH_TLink, int >::iterator link_nb;
4164       // put all elements links to linkNbMap
4165       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4166       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4167         const SMDS_MeshElement* elem = (*elemIt);
4168         int nbn =  elem->NbCornerNodes();
4169         // loop on elem links: insert them in linkNbMap
4170         for ( int iN = 0; iN < nbn; ++iN ) {
4171           const SMDS_MeshNode* n1 = elem->GetNode( iN );
4172           const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
4173           SMESH_TLink link( n1, n2 );
4174           link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
4175           link_nb->second++;
4176         }
4177       }
4178       // remove nodes that are in links encountered only once from setMovableNodes
4179       for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
4180         if ( link_nb->second == 1 ) {
4181           setMovableNodes.erase( link_nb->first.node1() );
4182           setMovableNodes.erase( link_nb->first.node2() );
4183         }
4184       }
4185     }
4186
4187     // -----------------------------------------------------
4188     // for nodes on seam edge, compute one more UV ( uvMap2 );
4189     // find movable nodes linked to nodes on seam and which
4190     // are to be smoothed using the second UV ( uvMap2 )
4191     // -----------------------------------------------------
4192
4193     set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
4194     if ( !surface.IsNull() ) {
4195       TopExp_Explorer eExp( face, TopAbs_EDGE );
4196       for ( ; eExp.More(); eExp.Next() ) {
4197         TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
4198         if ( !BRep_Tool::IsClosed( edge, face ))
4199           continue;
4200         SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
4201         if ( !sm ) continue;
4202         // find out which parameter varies for a node on seam
4203         double f,l;
4204         gp_Pnt2d uv1, uv2;
4205         Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
4206         if ( pcurve.IsNull() ) continue;
4207         uv1 = pcurve->Value( f );
4208         edge.Reverse();
4209         pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
4210         if ( pcurve.IsNull() ) continue;
4211         uv2 = pcurve->Value( f );
4212         int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
4213         // assure uv1 < uv2
4214         if ( uv1.Coord( iPar ) > uv2.Coord( iPar ))
4215           std::swap( uv1, uv2 );
4216         // get nodes on seam and its vertices
4217         list< const SMDS_MeshNode* > seamNodes;
4218         SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
4219         while ( nSeamIt->more() ) {
4220           const SMDS_MeshNode* node = nSeamIt->next();
4221           if ( !isQuadratic || !IsMedium( node ))
4222             seamNodes.push_back( node );
4223         }
4224         TopExp_Explorer vExp( edge, TopAbs_VERTEX );
4225         for ( ; vExp.More(); vExp.Next() ) {
4226           sm = aMesh->MeshElements( vExp.Current() );
4227           if ( sm ) {
4228             nSeamIt = sm->GetNodes();
4229             while ( nSeamIt->more() )
4230               seamNodes.push_back( nSeamIt->next() );
4231           }
4232         }
4233         // loop on nodes on seam
4234         list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
4235         for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
4236           const SMDS_MeshNode* nSeam = *noSeIt;
4237           map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
4238           if ( n_uv == uvMap.end() )
4239             continue;
4240           // set the first UV
4241           n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
4242           // set the second UV
4243           listUV.push_back( *n_uv->second );
4244           listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
4245           if ( uvMap2.empty() )
4246             uvMap2 = uvMap; // copy the uvMap contents
4247           uvMap2[ nSeam ] = &listUV.back();
4248
4249           // collect movable nodes linked to ones on seam in nodesNearSeam
4250           SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
4251           while ( eIt->more() ) {
4252             const SMDS_MeshElement* e = eIt->next();
4253             int nbUseMap1 = 0, nbUseMap2 = 0;
4254             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4255             int nn = 0, nbn =  e->NbNodes();
4256             if(e->IsQuadratic()) nbn = nbn/2;
4257             while ( nn++ < nbn )
4258             {
4259               const SMDS_MeshNode* n =
4260                 static_cast<const SMDS_MeshNode*>( nIt->next() );
4261               if (n == nSeam ||
4262                   setMovableNodes.find( n ) == setMovableNodes.end() )
4263                 continue;
4264               // add only nodes being closer to uv2 than to uv1
4265               // gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
4266               //              0.5 * ( n->Y() + nSeam->Y() ),
4267               //              0.5 * ( n->Z() + nSeam->Z() ));
4268               // gp_XY uv;
4269               // getClosestUV( projector, pMid, uv );
4270               double x = uvMap[ n ]->Coord( iPar );
4271               if ( Abs( uv1.Coord( iPar ) - x ) >
4272                    Abs( uv2.Coord( iPar ) - x )) {
4273                 nodesNearSeam.insert( n );
4274                 nbUseMap2++;
4275               }
4276               else
4277                 nbUseMap1++;
4278             }
4279             // for centroidalSmooth all element nodes must
4280             // be on one side of a seam
4281             if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
4282               SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4283               nn = 0;
4284               while ( nn++ < nbn ) {
4285                 const SMDS_MeshNode* n =
4286                   static_cast<const SMDS_MeshNode*>( nIt->next() );
4287                 setMovableNodes.erase( n );
4288               }
4289             }
4290           }
4291         } // loop on nodes on seam
4292       } // loop on edge of a face
4293     } // if ( !face.IsNull() )
4294
4295     if ( setMovableNodes.empty() ) {
4296       MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
4297       continue; // goto next face
4298     }
4299
4300     // -------------
4301     // SMOOTHING //
4302     // -------------
4303
4304     int it = -1;
4305     double maxRatio = -1., maxDisplacement = -1.;
4306     set<const SMDS_MeshNode*>::iterator nodeToMove;
4307     for ( it = 0; it < theNbIterations; it++ ) {
4308       maxDisplacement = 0.;
4309       nodeToMove = setMovableNodes.begin();
4310       for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4311         const SMDS_MeshNode* node = (*nodeToMove);
4312         gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
4313
4314         // smooth
4315         bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
4316         if ( theSmoothMethod == LAPLACIAN )
4317           laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
4318         else
4319           centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
4320
4321         // node displacement
4322         gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
4323         Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
4324         if ( aDispl > maxDisplacement )
4325           maxDisplacement = aDispl;
4326       }
4327       // no node movement => exit
4328       //if ( maxDisplacement < 1.e-16 ) {
4329       if ( maxDisplacement < disttol ) {
4330         MESSAGE("-- no node movement --");
4331         break;
4332       }
4333
4334       // check elements quality
4335       maxRatio  = 0;
4336       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4337       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4338         const SMDS_MeshElement* elem = (*elemIt);
4339         if ( !elem || elem->GetType() != SMDSAbs_Face )
4340           continue;
4341         SMESH::Controls::TSequenceOfXYZ aPoints;
4342         if ( aQualityFunc.GetPoints( elem, aPoints )) {
4343           double aValue = aQualityFunc.GetValue( aPoints );
4344           if ( aValue > maxRatio )
4345             maxRatio = aValue;
4346         }
4347       }
4348       if ( maxRatio <= theTgtAspectRatio ) {
4349         MESSAGE("-- quality achived --");
4350         break;
4351       }
4352       if (it+1 == theNbIterations) {
4353         MESSAGE("-- Iteration limit exceeded --");
4354       }
4355     } // smoothing iterations
4356
4357     MESSAGE(" Face id: " << *fId <<
4358             " Nb iterstions: " << it <<
4359             " Displacement: " << maxDisplacement <<
4360             " Aspect Ratio " << maxRatio);
4361
4362     // ---------------------------------------
4363     // new nodes positions are computed,
4364     // record movement in DS and set new UV
4365     // ---------------------------------------
4366     nodeToMove = setMovableNodes.begin();
4367     for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4368       SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
4369       aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
4370       map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
4371       if ( node_uv != uvMap.end() ) {
4372         gp_XY* uv = node_uv->second;
4373         node->SetPosition
4374           ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
4375       }
4376     }
4377
4378     // move medium nodes of quadratic elements
4379     if ( isQuadratic )
4380     {
4381       vector<const SMDS_MeshNode*> nodes;
4382       bool checkUV;
4383       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4384       for ( ; elemIt != elemsOnFace.end(); ++elemIt )
4385       {
4386         const SMDS_MeshElement* QF = *elemIt;
4387         if ( QF->IsQuadratic() )
4388         {
4389           nodes.assign( SMDS_MeshElement::iterator( QF->interlacedNodesElemIterator() ),
4390                         SMDS_MeshElement::iterator() );
4391           nodes.push_back( nodes[0] );
4392           gp_Pnt xyz;
4393           for (size_t i = 1; i < nodes.size(); i += 2 ) // i points to a medium node
4394           {
4395             if ( !surface.IsNull() )
4396             {
4397               gp_XY uv1 = helper.GetNodeUV( face, nodes[i-1], nodes[i+1], &checkUV );
4398               gp_XY uv2 = helper.GetNodeUV( face, nodes[i+1], nodes[i-1], &checkUV );
4399               gp_XY uv  = helper.GetMiddleUV( surface, uv1, uv2 );
4400               xyz = surface->Value( uv.X(), uv.Y() );
4401             }
4402             else {
4403               xyz = 0.5 * ( SMESH_TNodeXYZ( nodes[i-1] ) + SMESH_TNodeXYZ( nodes[i+1] ));
4404             }
4405             if (( SMESH_TNodeXYZ( nodes[i] ) - xyz.XYZ() ).Modulus() > disttol )
4406               // we have to move a medium node
4407               aMesh->MoveNode( nodes[i], xyz.X(), xyz.Y(), xyz.Z() );
4408           }
4409         }
4410       }
4411     }
4412
4413   } // loop on face ids
4414
4415 }
4416
4417 namespace
4418 {
4419   //=======================================================================
4420   //function : isReverse
4421   //purpose  : Return true if normal of prevNodes is not co-directied with
4422   //           gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
4423   //           iNotSame is where prevNodes and nextNodes are different.
4424   //           If result is true then future volume orientation is OK
4425   //=======================================================================
4426
4427   bool isReverse(const SMDS_MeshElement*             face,
4428                  const vector<const SMDS_MeshNode*>& prevNodes,
4429                  const vector<const SMDS_MeshNode*>& nextNodes,
4430                  const int                           iNotSame)
4431   {
4432
4433     SMESH_TNodeXYZ pP = prevNodes[ iNotSame ];
4434     SMESH_TNodeXYZ pN = nextNodes[ iNotSame ];
4435     gp_XYZ extrDir( pN - pP ), faceNorm;
4436     SMESH_MeshAlgos::FaceNormal( face, faceNorm, /*normalized=*/false );
4437
4438     return faceNorm * extrDir < 0.0;
4439   }
4440
4441   //================================================================================
4442   /*!
4443    * \brief Assure that theElemSets[0] holds elements, not nodes
4444    */
4445   //================================================================================
4446
4447   void setElemsFirst( TIDSortedElemSet theElemSets[2] )
4448   {
4449     if ( !theElemSets[0].empty() &&
4450          (*theElemSets[0].begin())->GetType() == SMDSAbs_Node )
4451     {
4452       std::swap( theElemSets[0], theElemSets[1] );
4453     }
4454     else if ( !theElemSets[1].empty() &&
4455               (*theElemSets[1].begin())->GetType() != SMDSAbs_Node )
4456     {
4457       std::swap( theElemSets[0], theElemSets[1] );
4458     }
4459   }
4460 }
4461
4462 //=======================================================================
4463 /*!
4464  * \brief Create elements by sweeping an element
4465  * \param elem - element to sweep
4466  * \param newNodesItVec - nodes generated from each node of the element
4467  * \param newElems - generated elements
4468  * \param nbSteps - number of sweeping steps
4469  * \param srcElements - to append elem for each generated element
4470  */
4471 //=======================================================================
4472
4473 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement*               elem,
4474                                     const vector<TNodeOfNodeListMapItr> & newNodesItVec,
4475                                     list<const SMDS_MeshElement*>&        newElems,
4476                                     const size_t                          nbSteps,
4477                                     SMESH_SequenceOfElemPtr&              srcElements)
4478 {
4479   SMESHDS_Mesh* aMesh = GetMeshDS();
4480
4481   const int           nbNodes = elem->NbNodes();
4482   const int         nbCorners = elem->NbCornerNodes();
4483   SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
4484                                                           polyhedron creation !!! */
4485   // Loop on elem nodes:
4486   // find new nodes and detect same nodes indices
4487   vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
4488   vector<const SMDS_MeshNode*> prevNod( nbNodes );
4489   vector<const SMDS_MeshNode*> nextNod( nbNodes );
4490   vector<const SMDS_MeshNode*> midlNod( nbNodes );
4491
4492   int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
4493   vector<int> sames(nbNodes);
4494   vector<bool> isSingleNode(nbNodes);
4495
4496   for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4497     TNodeOfNodeListMapItr                        nnIt = newNodesItVec[ iNode ];
4498     const SMDS_MeshNode*                         node = nnIt->first;
4499     const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
4500     if ( listNewNodes.empty() )
4501       return;
4502
4503     itNN   [ iNode ] = listNewNodes.begin();
4504     prevNod[ iNode ] = node;
4505     nextNod[ iNode ] = listNewNodes.front();
4506
4507     isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
4508                                                              corner node of linear */
4509     if ( prevNod[ iNode ] != nextNod [ iNode ])
4510       nbDouble += !isSingleNode[iNode];
4511
4512     if( iNode < nbCorners ) { // check corners only
4513       if ( prevNod[ iNode ] == nextNod [ iNode ])
4514         sames[nbSame++] = iNode;
4515       else
4516         iNotSameNode = iNode;
4517     }
4518   }
4519
4520   if ( nbSame == nbNodes || nbSame > 2) {
4521     MESSAGE( " Too many same nodes of element " << elem->GetID() );
4522     return;
4523   }
4524
4525   if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
4526   {
4527     // fix nodes order to have bottom normal external
4528     if ( baseType == SMDSEntity_Polygon )
4529     {
4530       std::reverse( itNN.begin(), itNN.end() );
4531       std::reverse( prevNod.begin(), prevNod.end() );
4532       std::reverse( midlNod.begin(), midlNod.end() );
4533       std::reverse( nextNod.begin(), nextNod.end() );
4534       std::reverse( isSingleNode.begin(), isSingleNode.end() );
4535     }
4536     else
4537     {
4538       const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType, nbNodes );
4539       SMDS_MeshCell::applyInterlace( ind, itNN );
4540       SMDS_MeshCell::applyInterlace( ind, prevNod );
4541       SMDS_MeshCell::applyInterlace( ind, nextNod );
4542       SMDS_MeshCell::applyInterlace( ind, midlNod );
4543       SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4544       if ( nbSame > 0 )
4545       {
4546         sames[nbSame] = iNotSameNode;
4547         for ( int j = 0; j <= nbSame; ++j )
4548           for ( size_t i = 0; i < ind.size(); ++i )
4549             if ( ind[i] == sames[j] )
4550             {
4551               sames[j] = i;
4552               break;
4553             }
4554         iNotSameNode = sames[nbSame];
4555       }
4556     }
4557   }
4558   else if ( elem->GetType() == SMDSAbs_Edge )
4559   {
4560     // orient a new face same as adjacent one
4561     int i1, i2;
4562     const SMDS_MeshElement* e;
4563     TIDSortedElemSet dummy;
4564     if (( e = SMESH_MeshAlgos::FindFaceInSet( nextNod[0], prevNod[0], dummy,dummy, &i1, &i2 )) ||
4565         ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[1], nextNod[1], dummy,dummy, &i1, &i2 )) ||
4566         ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[0], prevNod[1], dummy,dummy, &i1, &i2 )))
4567     {
4568       // there is an adjacent face, check order of nodes in it
4569       bool sameOrder = ( Abs( i2 - i1 ) == 1 ) ? ( i2 > i1 ) : ( i2 < i1 );
4570       if ( sameOrder )
4571       {
4572         std::swap( itNN[0],    itNN[1] );
4573         std::swap( prevNod[0], prevNod[1] );
4574         std::swap( nextNod[0], nextNod[1] );
4575         isSingleNode.swap( isSingleNode[0], isSingleNode[1] );
4576         if ( nbSame > 0 )
4577           sames[0] = 1 - sames[0];
4578         iNotSameNode = 1 - iNotSameNode;
4579       }
4580     }
4581   }
4582
4583   int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
4584   if ( nbSame > 0 ) {
4585     iSameNode    = sames[ nbSame-1 ];
4586     iBeforeSame  = ( iSameNode + nbCorners - 1 ) % nbCorners;
4587     iAfterSame   = ( iSameNode + 1 ) % nbCorners;
4588     iOpposSame   = ( iSameNode - 2 < 0  ? iSameNode + 2 : iSameNode - 2 );
4589   }
4590
4591   if ( baseType == SMDSEntity_Polygon )
4592   {
4593     if      ( nbNodes == 3 ) baseType = SMDSEntity_Triangle;
4594     else if ( nbNodes == 4 ) baseType = SMDSEntity_Quadrangle;
4595   }
4596   else if ( baseType == SMDSEntity_Quad_Polygon )
4597   {
4598     if      ( nbNodes == 6 ) baseType = SMDSEntity_Quad_Triangle;
4599     else if ( nbNodes == 8 ) baseType = SMDSEntity_Quad_Quadrangle;
4600   }
4601
4602   // make new elements
4603   for ( size_t iStep = 0; iStep < nbSteps; iStep++ )
4604   {
4605     // get next nodes
4606     for ( iNode = 0; iNode < nbNodes; iNode++ )
4607     {
4608       midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
4609       nextNod[ iNode ] = *itNN[ iNode ]++;
4610     }
4611
4612     SMDS_MeshElement* aNewElem = 0;
4613     /*if(!elem->IsPoly())*/ {
4614       switch ( baseType ) {
4615       case SMDSEntity_0D:
4616       case SMDSEntity_Node: { // sweep NODE
4617         if ( nbSame == 0 ) {
4618           if ( isSingleNode[0] )
4619             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
4620           else
4621             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
4622         }
4623         else
4624           return;
4625         break;
4626       }
4627       case SMDSEntity_Edge: { // sweep EDGE
4628         if ( nbDouble == 0 )
4629         {
4630           if ( nbSame == 0 ) // ---> quadrangle
4631             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4632                                       nextNod[ 1 ], nextNod[ 0 ] );
4633           else               // ---> triangle
4634             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4635                                       nextNod[ iNotSameNode ] );
4636         }
4637         else                 // ---> polygon
4638         {
4639           vector<const SMDS_MeshNode*> poly_nodes;
4640           poly_nodes.push_back( prevNod[0] );
4641           poly_nodes.push_back( prevNod[1] );
4642           if ( prevNod[1] != nextNod[1] )
4643           {
4644             if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
4645             poly_nodes.push_back( nextNod[1] );
4646           }
4647           if ( prevNod[0] != nextNod[0] )
4648           {
4649             poly_nodes.push_back( nextNod[0] );
4650             if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
4651           }
4652           switch ( poly_nodes.size() ) {
4653           case 3:
4654             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
4655             break;
4656           case 4:
4657             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
4658                                        poly_nodes[ 2 ], poly_nodes[ 3 ]);
4659             break;
4660           default:
4661             aNewElem = aMesh->AddPolygonalFace (poly_nodes);
4662           }
4663         }
4664         break;
4665       }
4666       case SMDSEntity_Triangle: // TRIANGLE --->
4667         {
4668           if ( nbDouble > 0 ) break;
4669           if ( nbSame == 0 )       // ---> pentahedron
4670             aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4671                                          nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
4672
4673           else if ( nbSame == 1 )  // ---> pyramid
4674             aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4675                                          nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
4676                                          nextNod[ iSameNode ]);
4677
4678           else // 2 same nodes:       ---> tetrahedron
4679             aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4680                                          nextNod[ iNotSameNode ]);
4681           break;
4682         }
4683       case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
4684         {
4685           if ( nbSame == 2 )
4686             return;
4687           if ( nbDouble+nbSame == 2 )
4688           {
4689             if(nbSame==0) {      // ---> quadratic quadrangle
4690               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4691                                         prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
4692             }
4693             else { //(nbSame==1) // ---> quadratic triangle
4694               if(sames[0]==2) {
4695                 return; // medium node on axis
4696               }
4697               else if(sames[0]==0)
4698                 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1],
4699                                           prevNod[2], midlNod[1], nextNod[2] );
4700               else // sames[0]==1
4701                 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[0],
4702                                           prevNod[2], nextNod[2], midlNod[0]);
4703             }
4704           }
4705           else if ( nbDouble == 3 )
4706           {
4707             if ( nbSame == 0 ) {  // ---> bi-quadratic quadrangle
4708               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4709                                         prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
4710             }
4711           }
4712           else
4713             return;
4714           break;
4715         }
4716       case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
4717         if ( nbDouble > 0 ) break;
4718
4719         if ( nbSame == 0 )       // ---> hexahedron
4720           aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
4721                                        nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
4722
4723         else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
4724           aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4725                                        nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
4726                                        nextNod[ iSameNode ]);
4727           newElems.push_back( aNewElem );
4728           aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ],  prevNod[ iOpposSame ],
4729                                        prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
4730                                        nextNod[ iOpposSame ],  nextNod[ iBeforeSame ] );
4731         }
4732         else if ( nbSame == 2 ) { // ---> pentahedron
4733           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
4734             // iBeforeSame is same too
4735             aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
4736                                          nextNod[ iOpposSame ],  prevNod[ iSameNode ],
4737                                          prevNod[ iAfterSame ],  nextNod[ iAfterSame ]);
4738           else
4739             // iAfterSame is same too
4740             aNewElem = aMesh->AddVolume (prevNod[ iSameNode ],   prevNod[ iBeforeSame ],
4741                                          nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
4742                                          prevNod[ iOpposSame ],  nextNod[ iOpposSame ]);
4743         }
4744         break;
4745       }
4746       case SMDSEntity_Quad_Triangle:  // sweep (Bi)Quadratic TRIANGLE --->
4747       case SMDSEntity_BiQuad_Triangle: /* ??? */ {
4748         if ( nbDouble+nbSame != 3 ) break;
4749         if(nbSame==0) {
4750           // --->  pentahedron with 15 nodes
4751           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4752                                        nextNod[0], nextNod[1], nextNod[2],
4753                                        prevNod[3], prevNod[4], prevNod[5],
4754                                        nextNod[3], nextNod[4], nextNod[5],
4755                                        midlNod[0], midlNod[1], midlNod[2]);
4756         }
4757         else if(nbSame==1) {
4758           // --->  2d order pyramid of 13 nodes
4759           int apex = iSameNode;
4760           int i0 = ( apex + 1 ) % nbCorners;
4761           int i1 = ( apex - 1 + nbCorners ) % nbCorners;
4762           int i0a = apex + 3;
4763           int i1a = i1 + 3;
4764           int i01 = i0 + 3;
4765           aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
4766                                       nextNod[i0], nextNod[i1], prevNod[apex],
4767                                       prevNod[i01], midlNod[i0],
4768                                       nextNod[i01], midlNod[i1],
4769                                       prevNod[i1a], prevNod[i0a],
4770                                       nextNod[i0a], nextNod[i1a]);
4771         }
4772         else if(nbSame==2) {
4773           // --->  2d order tetrahedron of 10 nodes
4774           int n1 = iNotSameNode;
4775           int n2 = ( n1 + 1             ) % nbCorners;
4776           int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
4777           int n12 = n1 + 3;
4778           int n23 = n2 + 3;
4779           int n31 = n3 + 3;
4780           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
4781                                        prevNod[n12], prevNod[n23], prevNod[n31],
4782                                        midlNod[n1], nextNod[n12], nextNod[n31]);
4783         }
4784         break;
4785       }
4786       case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
4787         if( nbSame == 0 ) {
4788           if ( nbDouble != 4 ) break;
4789           // --->  hexahedron with 20 nodes
4790           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4791                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4792                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4793                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4794                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
4795         }
4796         else if(nbSame==1) {
4797           // ---> pyramid + pentahedron - can not be created since it is needed
4798           // additional middle node at the center of face
4799           //INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
4800           return;
4801         }
4802         else if( nbSame == 2 ) {
4803           if ( nbDouble != 2 ) break;
4804           // --->  2d order Pentahedron with 15 nodes
4805           int n1,n2,n4,n5;
4806           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
4807             // iBeforeSame is same too
4808             n1 = iBeforeSame;
4809             n2 = iOpposSame;
4810             n4 = iSameNode;
4811             n5 = iAfterSame;
4812           }
4813           else {
4814             // iAfterSame is same too
4815             n1 = iSameNode;
4816             n2 = iBeforeSame;
4817             n4 = iAfterSame;
4818             n5 = iOpposSame;
4819           }
4820           int n12 = n2 + 4;
4821           int n45 = n4 + 4;
4822           int n14 = n1 + 4;
4823           int n25 = n5 + 4;
4824           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
4825                                        prevNod[n4], prevNod[n5], nextNod[n5],
4826                                        prevNod[n12], midlNod[n2], nextNod[n12],
4827                                        prevNod[n45], midlNod[n5], nextNod[n45],
4828                                        prevNod[n14], prevNod[n25], nextNod[n25]);
4829         }
4830         break;
4831       }
4832       case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
4833
4834         if( nbSame == 0 && nbDouble == 9 ) {
4835           // --->  tri-quadratic hexahedron with 27 nodes
4836           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4837                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4838                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4839                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4840                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4841                                        prevNod[8], // bottom center
4842                                        midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4843                                        nextNod[8], // top center
4844                                        midlNod[8]);// elem center
4845         }
4846         else
4847         {
4848           return;
4849         }
4850         break;
4851       }
4852       case SMDSEntity_Polygon: { // sweep POLYGON
4853
4854         if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4855           // --->  hexagonal prism
4856           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4857                                        prevNod[3], prevNod[4], prevNod[5],
4858                                        nextNod[0], nextNod[1], nextNod[2],
4859                                        nextNod[3], nextNod[4], nextNod[5]);
4860         }
4861         break;
4862       }
4863       case SMDSEntity_Ball:
4864         return;
4865
4866       default:
4867         break;
4868       } // switch ( baseType )
4869     } // scope
4870
4871     if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4872     {
4873       if ( baseType != SMDSEntity_Polygon )
4874       {
4875         const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType,nbNodes);
4876         SMDS_MeshCell::applyInterlace( ind, prevNod );
4877         SMDS_MeshCell::applyInterlace( ind, nextNod );
4878         SMDS_MeshCell::applyInterlace( ind, midlNod );
4879         SMDS_MeshCell::applyInterlace( ind, itNN );
4880         SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4881         baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4882       }
4883       vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4884       vector<int> quantities (nbNodes + 2);
4885       polyedre_nodes.clear();
4886       quantities.clear();
4887
4888       // bottom of prism
4889       for (int inode = 0; inode < nbNodes; inode++)
4890         polyedre_nodes.push_back( prevNod[inode] );
4891       quantities.push_back( nbNodes );
4892
4893       // top of prism
4894       polyedre_nodes.push_back( nextNod[0] );
4895       for (int inode = nbNodes; inode-1; --inode )
4896         polyedre_nodes.push_back( nextNod[inode-1] );
4897       quantities.push_back( nbNodes );
4898
4899       // side faces
4900       // 3--6--2
4901       // |     |
4902       // 7     5
4903       // |     |
4904       // 0--4--1
4905       const int iQuad = elem->IsQuadratic();
4906       for (int iface = 0; iface < nbNodes; iface += 1+iQuad )
4907       {
4908         const int prevNbNodes = polyedre_nodes.size(); // to detect degenerated face
4909         int inextface = (iface+1+iQuad) % nbNodes;
4910         int imid      = (iface+1) % nbNodes;
4911         polyedre_nodes.push_back( prevNod[inextface] );         // 0
4912         if ( iQuad ) polyedre_nodes.push_back( prevNod[imid] ); // 4
4913         polyedre_nodes.push_back( prevNod[iface] );             // 1
4914         if ( prevNod[iface] != nextNod[iface] ) // 1 != 2
4915         {
4916           if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]); // 5
4917           polyedre_nodes.push_back( nextNod[iface] );                         // 2
4918         }
4919         if ( iQuad ) polyedre_nodes.push_back( nextNod[imid] );               // 6
4920         if ( prevNod[inextface] != nextNod[inextface] ) // 0 != 3
4921         {
4922           polyedre_nodes.push_back( nextNod[inextface] );                            // 3
4923           if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);// 7
4924         }
4925         const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4926         if ( nbFaceNodes > 2 )
4927           quantities.push_back( nbFaceNodes );
4928         else // degenerated face
4929           polyedre_nodes.resize( prevNbNodes );
4930       }
4931       aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4932
4933     } // try to create a polyherdal prism
4934
4935     if ( aNewElem ) {
4936       newElems.push_back( aNewElem );
4937       myLastCreatedElems.Append(aNewElem);
4938       srcElements.Append( elem );
4939     }
4940
4941     // set new prev nodes
4942     for ( iNode = 0; iNode < nbNodes; iNode++ )
4943       prevNod[ iNode ] = nextNod[ iNode ];
4944
4945   } // loop on steps
4946 }
4947
4948 //=======================================================================
4949 /*!
4950  * \brief Create 1D and 2D elements around swept elements
4951  * \param mapNewNodes - source nodes and ones generated from them
4952  * \param newElemsMap - source elements and ones generated from them
4953  * \param elemNewNodesMap - nodes generated from each node of each element
4954  * \param elemSet - all swept elements
4955  * \param nbSteps - number of sweeping steps
4956  * \param srcElements - to append elem for each generated element
4957  */
4958 //=======================================================================
4959
4960 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap &     mapNewNodes,
4961                                   TTElemOfElemListMap &    newElemsMap,
4962                                   TElemOfVecOfNnlmiMap &   elemNewNodesMap,
4963                                   TIDSortedElemSet&        elemSet,
4964                                   const int                nbSteps,
4965                                   SMESH_SequenceOfElemPtr& srcElements)
4966 {
4967   ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4968   SMESHDS_Mesh* aMesh = GetMeshDS();
4969
4970   // Find nodes belonging to only one initial element - sweep them into edges.
4971
4972   TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4973   for ( ; nList != mapNewNodes.end(); nList++ )
4974   {
4975     const SMDS_MeshNode* node =
4976       static_cast<const SMDS_MeshNode*>( nList->first );
4977     if ( newElemsMap.count( node ))
4978       continue; // node was extruded into edge
4979     SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4980     int nbInitElems = 0;
4981     const SMDS_MeshElement* el = 0;
4982     SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4983     while ( eIt->more() && nbInitElems < 2 ) {
4984       const SMDS_MeshElement* e = eIt->next();
4985       SMDSAbs_ElementType  type = e->GetType();
4986       if ( type == SMDSAbs_Volume ||
4987            type < highType ||
4988            !elemSet.count(e))
4989         continue;
4990       if ( type > highType ) {
4991         nbInitElems = 0;
4992         highType    = type;
4993       }
4994       el = e;
4995       ++nbInitElems;
4996     }
4997     if ( nbInitElems == 1 ) {
4998       bool NotCreateEdge = el && el->IsMediumNode(node);
4999       if(!NotCreateEdge) {
5000         vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
5001         list<const SMDS_MeshElement*> newEdges;
5002         sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
5003       }
5004     }
5005   }
5006
5007   // Make a ceiling for each element ie an equal element of last new nodes.
5008   // Find free links of faces - make edges and sweep them into faces.
5009
5010   ElemFeatures polyFace( SMDSAbs_Face, /*isPoly=*/true ), anyFace;
5011
5012   TTElemOfElemListMap::iterator  itElem      = newElemsMap.begin();
5013   TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
5014   for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
5015   {
5016     const SMDS_MeshElement* elem = itElem->first;
5017     vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
5018
5019     if(itElem->second.size()==0) continue;
5020
5021     const bool isQuadratic = elem->IsQuadratic();
5022
5023     if ( elem->GetType() == SMDSAbs_Edge ) {
5024       // create a ceiling edge
5025       if ( !isQuadratic ) {
5026         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
5027                                vecNewNodes[ 1 ]->second.back())) {
5028           myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
5029                                                    vecNewNodes[ 1 ]->second.back()));
5030           srcElements.Append( elem );
5031         }
5032       }
5033       else {
5034         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
5035                                vecNewNodes[ 1 ]->second.back(),
5036                                vecNewNodes[ 2 ]->second.back())) {
5037           myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
5038                                                    vecNewNodes[ 1 ]->second.back(),
5039                                                    vecNewNodes[ 2 ]->second.back()));
5040           srcElements.Append( elem );
5041         }
5042       }
5043     }
5044     if ( elem->GetType() != SMDSAbs_Face )
5045       continue;
5046
5047     bool hasFreeLinks = false;
5048
5049     TIDSortedElemSet avoidSet;
5050     avoidSet.insert( elem );
5051
5052     set<const SMDS_MeshNode*> aFaceLastNodes;
5053     int iNode, nbNodes = vecNewNodes.size();
5054     if ( !isQuadratic ) {
5055       // loop on the face nodes
5056       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
5057         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5058         // look for free links of the face
5059         int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
5060         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
5061         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
5062         // check if a link n1-n2 is free
5063         if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
5064           hasFreeLinks = true;
5065           // make a new edge and a ceiling for a new edge
5066           const SMDS_MeshElement* edge;
5067           if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
5068             myLastCreatedElems.Append( edge = aMesh->AddEdge( n1, n2 )); // free link edge
5069             srcElements.Append( myLastCreatedElems.Last() );
5070           }
5071           n1 = vecNewNodes[ iNode ]->second.back();
5072           n2 = vecNewNodes[ iNext ]->second.back();
5073           if ( !aMesh->FindEdge( n1, n2 )) {
5074             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // new edge ceiling
5075             srcElements.Append( edge );
5076           }
5077         }
5078       }
5079     }
5080     else { // elem is quadratic face
5081       int nbn = nbNodes/2;
5082       for ( iNode = 0; iNode < nbn; iNode++ ) {
5083         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5084         int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
5085         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
5086         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
5087         const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
5088         // check if a link is free
5089         if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
5090              ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
5091              ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
5092           hasFreeLinks = true;
5093           // make an edge and a ceiling for a new edge
5094           // find medium node
5095           if ( !aMesh->FindEdge( n1, n2, n3 )) {
5096             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // free link edge
5097             srcElements.Append( elem );
5098           }
5099           n1 = vecNewNodes[ iNode ]->second.back();
5100           n2 = vecNewNodes[ iNext ]->second.back();
5101           n3 = vecNewNodes[ iNode+nbn ]->second.back();
5102           if ( !aMesh->FindEdge( n1, n2, n3 )) {
5103             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
5104             srcElements.Append( elem );
5105           }
5106         }
5107       }
5108       for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
5109         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5110       }
5111     }
5112
5113     // sweep free links into faces
5114
5115     if ( hasFreeLinks ) {
5116       list<const SMDS_MeshElement*> & newVolumes = itElem->second;
5117       int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
5118
5119       set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
5120       set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
5121       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
5122         initNodeSet.insert( vecNewNodes[ iNode ]->first );
5123         topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
5124       }
5125       if ( isQuadratic && nbNodes % 2 ) {  // node set for the case of a biquadratic
5126         initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
5127         initNodeSetNoCenter.erase( vecNewNodes.back()->first );
5128       }
5129       for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
5130         list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
5131         std::advance( v, volNb );
5132         // find indices of free faces of a volume and their source edges
5133         list< int > freeInd;
5134         list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
5135         SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
5136         int iF, nbF = vTool.NbFaces();
5137         for ( iF = 0; iF < nbF; iF ++ ) {
5138           if (vTool.IsFreeFace( iF ) &&
5139               vTool.GetFaceNodes( iF, faceNodeSet ) &&
5140               initNodeSet != faceNodeSet) // except an initial face
5141           {
5142             if ( nbSteps == 1 && faceNodeSet == topNodeSet )
5143               continue;
5144             if ( faceNodeSet == initNodeSetNoCenter )
5145               continue;
5146             freeInd.push_back( iF );
5147             // find source edge of a free face iF
5148             vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
5149             vector<const SMDS_MeshNode*>::iterator lastCommom;
5150             commonNodes.resize( nbNodes, 0 );
5151             lastCommom = std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
5152                                                 initNodeSet.begin(), initNodeSet.end(),
5153                                                 commonNodes.begin());
5154             if ( std::distance( commonNodes.begin(), lastCommom ) == 3 )
5155               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
5156             else
5157               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
5158 #ifdef _DEBUG_
5159             if ( !srcEdges.back() )
5160             {
5161               cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
5162                    << iF << " of volume #" << vTool.ID() << endl;
5163             }
5164 #endif
5165           }
5166         }
5167         if ( freeInd.empty() )
5168           continue;
5169
5170         // create wall faces for all steps;
5171         // if such a face has been already created by sweep of edge,
5172         // assure that its orientation is OK
5173         for ( int iStep = 0; iStep < nbSteps; iStep++ )
5174         {
5175           vTool.Set( *v, /*ignoreCentralNodes=*/false );
5176           vTool.SetExternalNormal();
5177           const int nextShift = vTool.IsForward() ? +1 : -1;
5178           list< int >::iterator ind = freeInd.begin();
5179           list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
5180           for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
5181           {
5182             const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
5183             int nbn = vTool.NbFaceNodes( *ind );
5184             const SMDS_MeshElement * f = 0;
5185             if ( nbn == 3 )              ///// triangle
5186             {
5187               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
5188               if ( !f ||
5189                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
5190               {
5191                 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
5192                                                      nodes[ 1 ],
5193                                                      nodes[ 1 + nextShift ] };
5194                 if ( f )
5195                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5196                 else
5197                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
5198                                                             newOrder[ 2 ] ));
5199               }
5200             }
5201             else if ( nbn == 4 )       ///// quadrangle
5202             {
5203               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
5204               if ( !f ||
5205                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
5206               {
5207                 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
5208                                                      nodes[ 2 ], nodes[ 2+nextShift ] };
5209                 if ( f )
5210                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5211                 else
5212                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
5213                                                             newOrder[ 2 ], newOrder[ 3 ]));
5214               }
5215             }
5216             else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
5217             {
5218               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
5219               if ( !f ||
5220                    nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
5221               {
5222                 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
5223                                                      nodes[2],
5224                                                      nodes[2 + 2*nextShift],
5225                                                      nodes[3 - 2*nextShift],
5226                                                      nodes[3],
5227                                                      nodes[3 + 2*nextShift]};
5228                 if ( f )
5229                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5230                 else
5231                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ],
5232                                                             newOrder[ 1 ],
5233                                                             newOrder[ 2 ],
5234                                                             newOrder[ 3 ],
5235                                                             newOrder[ 4 ],
5236                                                             newOrder[ 5 ] ));
5237               }
5238             }
5239             else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
5240             {
5241               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
5242                                    nodes[1], nodes[3], nodes[5], nodes[7] );
5243               if ( !f ||
5244                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5245               {
5246                 const SMDS_MeshNode* newOrder[8] = { nodes[0],
5247                                                      nodes[4 - 2*nextShift],
5248                                                      nodes[4],
5249                                                      nodes[4 + 2*nextShift],
5250                                                      nodes[1],
5251                                                      nodes[5 - 2*nextShift],
5252                                                      nodes[5],
5253                                                      nodes[5 + 2*nextShift] };
5254                 if ( f )
5255                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5256                 else
5257                   myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5258                                                            newOrder[ 2 ], newOrder[ 3 ],
5259                                                            newOrder[ 4 ], newOrder[ 5 ],
5260                                                            newOrder[ 6 ], newOrder[ 7 ]));
5261               }
5262             }
5263             else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
5264             {
5265               f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
5266                                       SMDSAbs_Face, /*noMedium=*/false);
5267               if ( !f ||
5268                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5269               {
5270                 const SMDS_MeshNode* newOrder[9] = { nodes[0],
5271                                                      nodes[4 - 2*nextShift],
5272                                                      nodes[4],
5273                                                      nodes[4 + 2*nextShift],
5274                                                      nodes[1],
5275                                                      nodes[5 - 2*nextShift],
5276                                                      nodes[5],
5277                                                      nodes[5 + 2*nextShift],
5278                                                      nodes[8] };
5279                 if ( f )
5280                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5281                 else
5282                   myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5283                                                            newOrder[ 2 ], newOrder[ 3 ],
5284                                                            newOrder[ 4 ], newOrder[ 5 ],
5285                                                            newOrder[ 6 ], newOrder[ 7 ],
5286                                                            newOrder[ 8 ]));
5287               }
5288             }
5289             else  //////// polygon
5290             {
5291               vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
5292               const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
5293               if ( !f ||
5294                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
5295               {
5296                 if ( !vTool.IsForward() )
5297                   std::reverse( polygon_nodes.begin(), polygon_nodes.end());
5298                 if ( f )
5299                   aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
5300                 else
5301                   AddElement( polygon_nodes, polyFace.SetQuad( (*v)->IsQuadratic() ));
5302               }
5303             }
5304
5305             while ( srcElements.Length() < myLastCreatedElems.Length() )
5306               srcElements.Append( *srcEdge );
5307
5308           }  // loop on free faces
5309
5310           // go to the next volume
5311           iVol = 0;
5312           while ( iVol++ < nbVolumesByStep ) v++;
5313
5314         } // loop on steps
5315       } // loop on volumes of one step
5316     } // sweep free links into faces
5317
5318     // Make a ceiling face with a normal external to a volume
5319
5320     // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
5321     SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
5322     int iF = lastVol.GetFaceIndex( aFaceLastNodes );
5323
5324     if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
5325       aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
5326       iF = lastVol.GetFaceIndex( aFaceLastNodes );
5327     }
5328     if ( iF >= 0 )
5329     {
5330       lastVol.SetExternalNormal();
5331       const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
5332       const               int nbn = lastVol.NbFaceNodes( iF );
5333       vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
5334       if ( !hasFreeLinks ||
5335            !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
5336       {
5337         const vector<int>& interlace =
5338           SMDS_MeshCell::interlacedSmdsOrder( elem->GetEntityType(), nbn );
5339         SMDS_MeshCell::applyInterlaceRev( interlace, nodeVec );
5340
5341         AddElement( nodeVec, anyFace.Init( elem ));
5342
5343         while ( srcElements.Length() < myLastCreatedElems.Length() )
5344           srcElements.Append( elem );
5345       }
5346     }
5347   } // loop on swept elements
5348 }
5349
5350 //=======================================================================
5351 //function : RotationSweep
5352 //purpose  :
5353 //=======================================================================
5354
5355 SMESH_MeshEditor::PGroupIDs
5356 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet   theElemSets[2],
5357                                 const gp_Ax1&      theAxis,
5358                                 const double       theAngle,
5359                                 const int          theNbSteps,
5360                                 const double       theTol,
5361                                 const bool         theMakeGroups,
5362                                 const bool         theMakeWalls)
5363 {
5364   myLastCreatedElems.Clear();
5365   myLastCreatedNodes.Clear();
5366
5367   // source elements for each generated one
5368   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5369
5370   gp_Trsf aTrsf;
5371   aTrsf.SetRotation( theAxis, theAngle );
5372   gp_Trsf aTrsf2;
5373   aTrsf2.SetRotation( theAxis, theAngle/2. );
5374
5375   gp_Lin aLine( theAxis );
5376   double aSqTol = theTol * theTol;
5377
5378   SMESHDS_Mesh* aMesh = GetMeshDS();
5379
5380   TNodeOfNodeListMap mapNewNodes;
5381   TElemOfVecOfNnlmiMap mapElemNewNodes;
5382   TTElemOfElemListMap newElemsMap;
5383
5384   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5385                                      myMesh->NbFaces(ORDER_QUADRATIC) +
5386                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
5387   // loop on theElemSets
5388   setElemsFirst( theElemSets );
5389   TIDSortedElemSet::iterator itElem;
5390   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5391   {
5392     TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5393     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5394       const SMDS_MeshElement* elem = *itElem;
5395       if ( !elem || elem->GetType() == SMDSAbs_Volume )
5396         continue;
5397       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5398       newNodesItVec.reserve( elem->NbNodes() );
5399
5400       // loop on elem nodes
5401       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5402       while ( itN->more() )
5403       {
5404         const SMDS_MeshNode* node = cast2Node( itN->next() );
5405
5406         gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
5407         double coord[3];
5408         aXYZ.Coord( coord[0], coord[1], coord[2] );
5409         bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
5410
5411         // check if a node has been already sweeped
5412         TNodeOfNodeListMapItr nIt =
5413           mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5414         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5415         if ( listNewNodes.empty() )
5416         {
5417           // check if we are to create medium nodes between corner ones
5418           bool needMediumNodes = false;
5419           if ( isQuadraticMesh )
5420           {
5421             SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5422             while (it->more() && !needMediumNodes )
5423             {
5424               const SMDS_MeshElement* invElem = it->next();
5425               if ( invElem != elem && !theElems.count( invElem )) continue;
5426               needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5427               if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5428                 needMediumNodes = true;
5429             }
5430           }
5431
5432           // make new nodes
5433           const SMDS_MeshNode * newNode = node;
5434           for ( int i = 0; i < theNbSteps; i++ ) {
5435             if ( !isOnAxis ) {
5436               if ( needMediumNodes )  // create a medium node
5437               {
5438                 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5439                 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5440                 myLastCreatedNodes.Append(newNode);
5441                 srcNodes.Append( node );
5442                 listNewNodes.push_back( newNode );
5443                 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5444               }
5445               else {
5446                 aTrsf.Transforms( coord[0], coord[1], coord[2] );
5447               }
5448               // create a corner node
5449               newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5450               myLastCreatedNodes.Append(newNode);
5451               srcNodes.Append( node );
5452               listNewNodes.push_back( newNode );
5453             }
5454             else {
5455               listNewNodes.push_back( newNode );
5456               // if ( needMediumNodes )
5457               //   listNewNodes.push_back( newNode );
5458             }
5459           }
5460         }
5461         newNodesItVec.push_back( nIt );
5462       }
5463       // make new elements
5464       sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
5465     }
5466   }
5467
5468   if ( theMakeWalls )
5469     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], theNbSteps, srcElems );
5470
5471   PGroupIDs newGroupIDs;
5472   if ( theMakeGroups )
5473     newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
5474
5475   return newGroupIDs;
5476 }
5477
5478 //=======================================================================
5479 //function : ExtrusParam
5480 //purpose  : standard construction
5481 //=======================================================================
5482
5483 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Vec&            theStep,
5484                                             const int                theNbSteps,
5485                                             const std::list<double>& theScales,
5486                                             const gp_XYZ*            theBasePoint,
5487                                             const int                theFlags,
5488                                             const double             theTolerance):
5489   myDir( theStep ),
5490   myBaseP( Precision::Infinite(), 0, 0 ),
5491   myFlags( theFlags ),
5492   myTolerance( theTolerance ),
5493   myElemsToUse( NULL )
5494 {
5495   mySteps = new TColStd_HSequenceOfReal;
5496   const double stepSize = theStep.Magnitude();
5497   for (int i=1; i<=theNbSteps; i++ )
5498     mySteps->Append( stepSize );
5499
5500   int nbScales = theScales.size();
5501   if ( nbScales > 0 )
5502   {
5503     if ( IsLinearVariation() && nbScales < theNbSteps )
5504     {
5505       myScales.reserve( theNbSteps );
5506       std::list<double>::const_iterator scale = theScales.begin();
5507       double prevScale = 1.0;
5508       for ( int iSc = 1; scale != theScales.end(); ++scale, ++iSc )
5509       {
5510         int      iStep = int( iSc / double( nbScales ) * theNbSteps + 0.5 );
5511         int    stDelta = Max( 1, iStep - myScales.size());
5512         double scDelta = ( *scale - prevScale ) / stDelta;
5513         for ( int iStep = 0; iStep < stDelta; ++iStep )
5514         {
5515           myScales.push_back( prevScale + scDelta );
5516           prevScale = myScales.back();
5517         }
5518         prevScale = *scale;
5519       }
5520     }
5521     else
5522     {
5523       myScales.assign( theScales.begin(), theScales.end() );
5524     }
5525   }
5526   if ( theBasePoint )
5527   {
5528     myBaseP = *theBasePoint;
5529   }
5530
5531   if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5532       ( theTolerance > 0 ))
5533   {
5534     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5535   }
5536   else
5537   {
5538     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5539   }
5540 }
5541
5542 //=======================================================================
5543 //function : ExtrusParam
5544 //purpose  : steps are given explicitly
5545 //=======================================================================
5546
5547 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Dir&                   theDir,
5548                                             Handle(TColStd_HSequenceOfReal) theSteps,
5549                                             const int                       theFlags,
5550                                             const double                    theTolerance):
5551   myDir( theDir ),
5552   mySteps( theSteps ),
5553   myFlags( theFlags ),
5554   myTolerance( theTolerance ),
5555   myElemsToUse( NULL )
5556 {
5557   if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5558       ( theTolerance > 0 ))
5559   {
5560     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5561   }
5562   else
5563   {
5564     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5565   }
5566 }
5567
5568 //=======================================================================
5569 //function : ExtrusParam
5570 //purpose  : for extrusion by normal
5571 //=======================================================================
5572
5573 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const double theStepSize,
5574                                             const int    theNbSteps,
5575                                             const int    theFlags,
5576                                             const int    theDim ):
5577   myDir( 1,0,0 ),
5578   mySteps( new TColStd_HSequenceOfReal ),
5579   myFlags( theFlags ),
5580   myTolerance( 0 ),
5581   myElemsToUse( NULL )
5582 {
5583   for (int i = 0; i < theNbSteps; i++ )
5584     mySteps->Append( theStepSize );
5585
5586   if ( theDim == 1 )
5587   {
5588     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal1D;
5589   }
5590   else
5591   {
5592     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal2D;
5593   }
5594 }
5595
5596 //=======================================================================
5597 //function : ExtrusParam::SetElementsToUse
5598 //purpose  : stores elements to use for extrusion by normal, depending on
5599 //           state of EXTRUSION_FLAG_USE_INPUT_ELEMS_ONLY flag;
5600 //           define myBaseP for scaling
5601 //=======================================================================
5602
5603 void SMESH_MeshEditor::ExtrusParam::SetElementsToUse( const TIDSortedElemSet& elems,
5604                                                       const TIDSortedElemSet& nodes )
5605 {
5606   myElemsToUse = ToUseInpElemsOnly() ? & elems : 0;
5607
5608   if ( Precision::IsInfinite( myBaseP.X() )) // myBaseP not defined
5609   {
5610     myBaseP.SetCoord( 0.,0.,0. );
5611     TIDSortedElemSet newNodes;
5612
5613     const TIDSortedElemSet* elemSets[] = { &elems, &nodes };
5614     for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5615     {
5616       const TIDSortedElemSet& elements = *( elemSets[ is2ndSet ]);
5617       TIDSortedElemSet::const_iterator itElem = elements.begin();
5618       for ( ; itElem != elements.end(); itElem++ )
5619       {
5620         const SMDS_MeshElement* elem = *itElem;
5621         SMDS_ElemIteratorPtr     itN = elem->nodesIterator();
5622         while ( itN->more() ) {
5623           const SMDS_MeshElement* node = itN->next();
5624           if ( newNodes.insert( node ).second )
5625             myBaseP += SMESH_TNodeXYZ( node );
5626         }
5627       }
5628     }
5629     myBaseP /= newNodes.size();
5630   }
5631 }
5632
5633 //=======================================================================
5634 //function : ExtrusParam::beginStepIter
5635 //purpose  : prepare iteration on steps
5636 //=======================================================================
5637
5638 void SMESH_MeshEditor::ExtrusParam::beginStepIter( bool withMediumNodes )
5639 {
5640   myWithMediumNodes = withMediumNodes;
5641   myNextStep = 1;
5642   myCurSteps.clear();
5643 }
5644 //=======================================================================
5645 //function : ExtrusParam::moreSteps
5646 //purpose  : are there more steps?
5647 //=======================================================================
5648
5649 bool SMESH_MeshEditor::ExtrusParam::moreSteps()
5650 {
5651   return myNextStep <= mySteps->Length() || !myCurSteps.empty();
5652 }
5653 //=======================================================================
5654 //function : ExtrusParam::nextStep
5655 //purpose  : returns the next step
5656 //=======================================================================
5657
5658 double SMESH_MeshEditor::ExtrusParam::nextStep()
5659 {
5660   double res = 0;
5661   if ( !myCurSteps.empty() )
5662   {
5663     res = myCurSteps.back();
5664     myCurSteps.pop_back();
5665   }
5666   else if ( myNextStep <= mySteps->Length() )
5667   {
5668     myCurSteps.push_back( mySteps->Value( myNextStep ));
5669     ++myNextStep;
5670     if ( myWithMediumNodes )
5671     {
5672       myCurSteps.back() /= 2.;
5673       myCurSteps.push_back( myCurSteps.back() );
5674     }
5675     res = nextStep();
5676   }
5677   return res;
5678 }
5679
5680 //=======================================================================
5681 //function : ExtrusParam::makeNodesByDir
5682 //purpose  : create nodes for standard extrusion
5683 //=======================================================================
5684
5685 int SMESH_MeshEditor::ExtrusParam::
5686 makeNodesByDir( SMESHDS_Mesh*                     mesh,
5687                 const SMDS_MeshNode*              srcNode,
5688                 std::list<const SMDS_MeshNode*> & newNodes,
5689                 const bool                        makeMediumNodes)
5690 {
5691   gp_XYZ p = SMESH_TNodeXYZ( srcNode );
5692
5693   int nbNodes = 0;
5694   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5695   {
5696     p += myDir.XYZ() * nextStep();
5697     const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5698     newNodes.push_back( newNode );
5699   }
5700
5701   if ( !myScales.empty() )
5702   {
5703     if ( makeMediumNodes && myMediumScales.empty() )
5704     {
5705       myMediumScales.resize( myScales.size() );
5706       double prevFactor = 1.;
5707       for ( size_t i = 0; i < myScales.size(); ++i )
5708       {
5709         myMediumScales[i] = 0.5 * ( prevFactor + myScales[i] );
5710         prevFactor = myScales[i];
5711       }
5712     }
5713     typedef std::vector<double>::iterator ScaleIt;
5714     ScaleIt scales[] = { myScales.begin(), myMediumScales.begin() };
5715
5716     size_t iSc = 0, nbScales = myScales.size() + myMediumScales.size();
5717
5718     gp_XYZ center = myBaseP;
5719     std::list<const SMDS_MeshNode*>::iterator nIt = newNodes.begin();
5720     size_t iN  = 0;
5721     for ( beginStepIter( makeMediumNodes ); moreSteps() && ( iN < nbScales ); ++nIt, ++iN )
5722     {
5723       center += myDir.XYZ() * nextStep();
5724
5725       iSc += int( makeMediumNodes );
5726       ScaleIt& scale = scales[ iSc % 2 ];
5727       
5728       gp_XYZ xyz = SMESH_TNodeXYZ( *nIt );
5729       xyz = ( *scale * ( xyz - center )) + center;
5730       mesh->MoveNode( *nIt, xyz.X(), xyz.Y(), xyz.Z() );
5731
5732       ++scale;
5733     }
5734   }
5735   return nbNodes;
5736 }
5737
5738 //=======================================================================
5739 //function : ExtrusParam::makeNodesByDirAndSew
5740 //purpose  : create nodes for standard extrusion with sewing
5741 //=======================================================================
5742
5743 int SMESH_MeshEditor::ExtrusParam::
5744 makeNodesByDirAndSew( SMESHDS_Mesh*                     mesh,
5745                       const SMDS_MeshNode*              srcNode,
5746                       std::list<const SMDS_MeshNode*> & newNodes,
5747                       const bool                        makeMediumNodes)
5748 {
5749   gp_XYZ P1 = SMESH_TNodeXYZ( srcNode );
5750
5751   int nbNodes = 0;
5752   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5753   {
5754     P1 += myDir.XYZ() * nextStep();
5755
5756     // try to search in sequence of existing nodes
5757     // if myNodes.Length()>0 we 'nave to use given sequence
5758     // else - use all nodes of mesh
5759     const SMDS_MeshNode * node = 0;
5760     if ( myNodes.Length() > 0 ) {
5761       int i;
5762       for(i=1; i<=myNodes.Length(); i++) {
5763         gp_XYZ P2 = SMESH_TNodeXYZ( myNodes.Value(i) );
5764         if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5765         {
5766           node = myNodes.Value(i);
5767           break;
5768         }
5769       }
5770     }
5771     else {
5772       SMDS_NodeIteratorPtr itn = mesh->nodesIterator();
5773       while(itn->more()) {
5774         SMESH_TNodeXYZ P2( itn->next() );
5775         if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5776         {
5777           node = P2._node;
5778           break;
5779         }
5780       }
5781     }
5782
5783     if ( !node )
5784       node = mesh->AddNode( P1.X(), P1.Y(), P1.Z() );
5785
5786     newNodes.push_back( node );
5787
5788   } // loop on steps
5789
5790   return nbNodes;
5791 }
5792
5793 //=======================================================================
5794 //function : ExtrusParam::makeNodesByNormal2D
5795 //purpose  : create nodes for extrusion using normals of faces
5796 //=======================================================================
5797
5798 int SMESH_MeshEditor::ExtrusParam::
5799 makeNodesByNormal2D( SMESHDS_Mesh*                     mesh,
5800                      const SMDS_MeshNode*              srcNode,
5801                      std::list<const SMDS_MeshNode*> & newNodes,
5802                      const bool                        makeMediumNodes)
5803 {
5804   const bool alongAvgNorm = ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL );
5805
5806   gp_XYZ p = SMESH_TNodeXYZ( srcNode );
5807
5808   // get normals to faces sharing srcNode
5809   vector< gp_XYZ > norms, baryCenters;
5810   gp_XYZ norm, avgNorm( 0,0,0 );
5811   SMDS_ElemIteratorPtr faceIt = srcNode->GetInverseElementIterator( SMDSAbs_Face );
5812   while ( faceIt->more() )
5813   {
5814     const SMDS_MeshElement* face = faceIt->next();
5815     if ( myElemsToUse && !myElemsToUse->count( face ))
5816       continue;
5817     if ( SMESH_MeshAlgos::FaceNormal( face, norm, /*normalized=*/true ))
5818     {
5819       norms.push_back( norm );
5820       avgNorm += norm;
5821       if ( !alongAvgNorm )
5822       {
5823         gp_XYZ bc(0,0,0);
5824         int nbN = 0;
5825         for ( SMDS_ElemIteratorPtr nIt = face->nodesIterator(); nIt->more(); ++nbN )
5826           bc += SMESH_TNodeXYZ( nIt->next() );
5827         baryCenters.push_back( bc / nbN );
5828       }
5829     }
5830   }
5831
5832   if ( norms.empty() ) return 0;
5833
5834   double normSize = avgNorm.Modulus();
5835   if ( normSize < std::numeric_limits<double>::min() )
5836     return 0;
5837
5838   if ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL ) // extrude along avgNorm
5839   {
5840     myDir = avgNorm;
5841     return makeNodesByDir( mesh, srcNode, newNodes, makeMediumNodes );
5842   }
5843
5844   avgNorm /= normSize;
5845
5846   int nbNodes = 0;
5847   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5848   {
5849     gp_XYZ pNew = p;
5850     double stepSize = nextStep();
5851
5852     if ( norms.size() > 1 )
5853     {
5854       for ( size_t iF = 0; iF < norms.size(); ++iF ) // loop on faces
5855       {
5856         // translate plane of a face
5857         baryCenters[ iF ] += norms[ iF ] * stepSize;
5858
5859         // find point of intersection of the face plane located at baryCenters[ iF ]
5860         // and avgNorm located at pNew
5861         double d    = -( norms[ iF ] * baryCenters[ iF ]); // d of plane equation ax+by+cz+d=0
5862         double dot  = ( norms[ iF ] * avgNorm );
5863         if ( dot < std::numeric_limits<double>::min() )
5864           dot = stepSize * 1e-3;
5865         double step = -( norms[ iF ] * pNew + d ) / dot;
5866         pNew += step * avgNorm;
5867       }
5868     }
5869     else
5870     {
5871       pNew += stepSize * avgNorm;
5872     }
5873     p = pNew;
5874
5875     const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5876     newNodes.push_back( newNode );
5877   }
5878   return nbNodes;
5879 }
5880
5881 //=======================================================================
5882 //function : ExtrusParam::makeNodesByNormal1D
5883 //purpose  : create nodes for extrusion using normals of edges
5884 //=======================================================================
5885
5886 int SMESH_MeshEditor::ExtrusParam::
5887 makeNodesByNormal1D( SMESHDS_Mesh*                     mesh,
5888                      const SMDS_MeshNode*              srcNode,
5889                      std::list<const SMDS_MeshNode*> & newNodes,
5890                      const bool                        makeMediumNodes)
5891 {
5892   throw SALOME_Exception("Extrusion 1D by Normal not implemented");
5893   return 0;
5894 }
5895
5896 //=======================================================================
5897 //function : ExtrusionSweep
5898 //purpose  :
5899 //=======================================================================
5900
5901 SMESH_MeshEditor::PGroupIDs
5902 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet     theElems[2],
5903                                   const gp_Vec&        theStep,
5904                                   const int            theNbSteps,
5905                                   TTElemOfElemListMap& newElemsMap,
5906                                   const int            theFlags,
5907                                   const double         theTolerance)
5908 {
5909   ExtrusParam aParams( theStep, theNbSteps, std::list<double>(), 0, theFlags, theTolerance );
5910   return ExtrusionSweep( theElems, aParams, newElemsMap );
5911 }
5912
5913
5914 //=======================================================================
5915 //function : ExtrusionSweep
5916 //purpose  :
5917 //=======================================================================
5918
5919 SMESH_MeshEditor::PGroupIDs
5920 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet     theElemSets[2],
5921                                   ExtrusParam&         theParams,
5922                                   TTElemOfElemListMap& newElemsMap)
5923 {
5924   myLastCreatedElems.Clear();
5925   myLastCreatedNodes.Clear();
5926
5927   // source elements for each generated one
5928   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5929
5930   setElemsFirst( theElemSets );
5931   const int nbSteps = theParams.NbSteps();
5932   theParams.SetElementsToUse( theElemSets[0], theElemSets[1] );
5933
5934   TNodeOfNodeListMap   mapNewNodes;
5935   TElemOfVecOfNnlmiMap mapElemNewNodes;
5936
5937   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5938                                      myMesh->NbFaces(ORDER_QUADRATIC) +
5939                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
5940   // loop on theElems
5941   TIDSortedElemSet::iterator itElem;
5942   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5943   {
5944     TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5945     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5946     {
5947       // check element type
5948       const SMDS_MeshElement* elem = *itElem;
5949       if ( !elem  || elem->GetType() == SMDSAbs_Volume )
5950         continue;
5951
5952       const size_t nbNodes = elem->NbNodes();
5953       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5954       newNodesItVec.reserve( nbNodes );
5955
5956       // loop on elem nodes
5957       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5958       while ( itN->more() )
5959       {
5960         // check if a node has been already sweeped
5961         const SMDS_MeshNode* node = cast2Node( itN->next() );
5962         TNodeOfNodeListMap::iterator nIt =
5963           mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5964         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5965         if ( listNewNodes.empty() )
5966         {
5967           // make new nodes
5968
5969           // check if we are to create medium nodes between corner ones
5970           bool needMediumNodes = false;
5971           if ( isQuadraticMesh )
5972           {
5973             SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5974             while (it->more() && !needMediumNodes )
5975             {
5976               const SMDS_MeshElement* invElem = it->next();
5977               if ( invElem != elem && !theElems.count( invElem )) continue;
5978               needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5979               if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5980                 needMediumNodes = true;
5981             }
5982           }
5983           // create nodes for all steps
5984           if ( theParams.MakeNodes( GetMeshDS(), node, listNewNodes, needMediumNodes ))
5985           {
5986             list<const SMDS_MeshNode*>::iterator newNodesIt = listNewNodes.begin();
5987             for ( ; newNodesIt != listNewNodes.end(); ++newNodesIt )
5988             {
5989               myLastCreatedNodes.Append( *newNodesIt );
5990               srcNodes.Append( node );
5991             }
5992           }
5993           else
5994           {
5995             break; // newNodesItVec will be shorter than nbNodes
5996           }
5997         }
5998         newNodesItVec.push_back( nIt );
5999       }
6000       // make new elements
6001       if ( newNodesItVec.size() == nbNodes )
6002         sweepElement( elem, newNodesItVec, newElemsMap[elem], nbSteps, srcElems );
6003     }
6004   }
6005
6006   if ( theParams.ToMakeBoundary() ) {
6007     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], nbSteps, srcElems );
6008   }
6009   PGroupIDs newGroupIDs;
6010   if ( theParams.ToMakeGroups() )
6011     newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
6012
6013   return newGroupIDs;
6014 }
6015
6016 //=======================================================================
6017 //function : ExtrusionAlongTrack
6018 //purpose  :
6019 //=======================================================================
6020 SMESH_MeshEditor::Extrusion_Error
6021 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet     theElements[2],
6022                                        SMESH_subMesh*       theTrack,
6023                                        const SMDS_MeshNode* theN1,
6024                                        const bool           theHasAngles,
6025                                        list<double>&        theAngles,
6026                                        const bool           theLinearVariation,
6027                                        const bool           theHasRefPoint,
6028                                        const gp_Pnt&        theRefPoint,
6029                                        const bool           theMakeGroups)
6030 {
6031   myLastCreatedElems.Clear();
6032   myLastCreatedNodes.Clear();
6033
6034   int aNbE;
6035   std::list<double> aPrms;
6036   TIDSortedElemSet::iterator itElem;
6037
6038   gp_XYZ aGC;
6039   TopoDS_Edge aTrackEdge;
6040   TopoDS_Vertex aV1, aV2;
6041
6042   SMDS_ElemIteratorPtr aItE;
6043   SMDS_NodeIteratorPtr aItN;
6044   SMDSAbs_ElementType aTypeE;
6045
6046   TNodeOfNodeListMap mapNewNodes;
6047
6048   // 1. Check data
6049   aNbE = theElements[0].size() + theElements[1].size();
6050   // nothing to do
6051   if ( !aNbE )
6052     return EXTR_NO_ELEMENTS;
6053
6054   // 1.1 Track Pattern
6055   ASSERT( theTrack );
6056
6057   SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS();
6058   if ( !pSubMeshDS )
6059     return ExtrusionAlongTrack( theElements, theTrack->GetFather(), theN1,
6060                                 theHasAngles, theAngles, theLinearVariation,
6061                                 theHasRefPoint, theRefPoint, theMakeGroups );
6062
6063   aItE = pSubMeshDS->GetElements();
6064   while ( aItE->more() ) {
6065     const SMDS_MeshElement* pE = aItE->next();
6066     aTypeE = pE->GetType();
6067     // Pattern must contain links only
6068     if ( aTypeE != SMDSAbs_Edge )
6069       return EXTR_PATH_NOT_EDGE;
6070   }
6071
6072   list<SMESH_MeshEditor_PathPoint> fullList;
6073
6074   const TopoDS_Shape& aS = theTrack->GetSubShape();
6075   // Sub-shape for the Pattern must be an Edge or Wire
6076   if( aS.ShapeType() == TopAbs_EDGE ) {
6077     aTrackEdge = TopoDS::Edge( aS );
6078     // the Edge must not be degenerated
6079     if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
6080       return EXTR_BAD_PATH_SHAPE;
6081     TopExp::Vertices( aTrackEdge, aV1, aV2 );
6082     aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
6083     const SMDS_MeshNode* aN1 = aItN->next();
6084     aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
6085     const SMDS_MeshNode* aN2 = aItN->next();
6086     // starting node must be aN1 or aN2
6087     if ( !( aN1 == theN1 || aN2 == theN1 ) )
6088       return EXTR_BAD_STARTING_NODE;
6089     aItN = pSubMeshDS->GetNodes();
6090     while ( aItN->more() ) {
6091       const SMDS_MeshNode* pNode = aItN->next();
6092       const SMDS_EdgePosition* pEPos =
6093         static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6094       double aT = pEPos->GetUParameter();
6095       aPrms.push_back( aT );
6096     }
6097     //Extrusion_Error err =
6098     MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
6099   } else if( aS.ShapeType() == TopAbs_WIRE ) {
6100     list< SMESH_subMesh* > LSM;
6101     TopTools_SequenceOfShape Edges;
6102     SMESH_subMeshIteratorPtr itSM = theTrack->getDependsOnIterator(false,true);
6103     while(itSM->more()) {
6104       SMESH_subMesh* SM = itSM->next();
6105       LSM.push_back(SM);
6106       const TopoDS_Shape& aS = SM->GetSubShape();
6107       Edges.Append(aS);
6108     }
6109     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6110     int startNid = theN1->GetID();
6111     TColStd_MapOfInteger UsedNums;
6112
6113     int NbEdges = Edges.Length();
6114     int i = 1;
6115     for(; i<=NbEdges; i++) {
6116       int k = 0;
6117       list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
6118       for(; itLSM!=LSM.end(); itLSM++) {
6119         k++;
6120         if(UsedNums.Contains(k)) continue;
6121         aTrackEdge = TopoDS::Edge( Edges.Value(k) );
6122         SMESH_subMesh* locTrack = *itLSM;
6123         SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
6124         TopExp::Vertices( aTrackEdge, aV1, aV2 );
6125         aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
6126         const SMDS_MeshNode* aN1 = aItN->next();
6127         aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
6128         const SMDS_MeshNode* aN2 = aItN->next();
6129         // starting node must be aN1 or aN2
6130         if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
6131         // 2. Collect parameters on the track edge
6132         aPrms.clear();
6133         aItN = locMeshDS->GetNodes();
6134         while ( aItN->more() ) {
6135           const SMDS_MeshNode* pNode = aItN->next();
6136           const SMDS_EdgePosition* pEPos =
6137             static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6138           double aT = pEPos->GetUParameter();
6139           aPrms.push_back( aT );
6140         }
6141         list<SMESH_MeshEditor_PathPoint> LPP;
6142         //Extrusion_Error err =
6143         MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
6144         LLPPs.push_back(LPP);
6145         UsedNums.Add(k);
6146         // update startN for search following egde
6147         if( aN1->GetID() == startNid ) startNid = aN2->GetID();
6148         else startNid = aN1->GetID();
6149         break;
6150       }
6151     }
6152     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6153     list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
6154     list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
6155     for(; itPP!=firstList.end(); itPP++) {
6156       fullList.push_back( *itPP );
6157     }
6158     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6159     fullList.pop_back();
6160     itLLPP++;
6161     for(; itLLPP!=LLPPs.end(); itLLPP++) {
6162       list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
6163       itPP = currList.begin();
6164       SMESH_MeshEditor_PathPoint PP2 = currList.front();
6165       gp_Dir D1 = PP1.Tangent();
6166       gp_Dir D2 = PP2.Tangent();
6167       gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
6168                            (D1.Z()+D2.Z())/2 ) );
6169       PP1.SetTangent(Dnew);
6170       fullList.push_back(PP1);
6171       itPP++;
6172       for(; itPP!=firstList.end(); itPP++) {
6173         fullList.push_back( *itPP );
6174       }
6175       PP1 = fullList.back();
6176       fullList.pop_back();
6177     }
6178     // if wire not closed
6179     fullList.push_back(PP1);
6180     // else ???
6181   }
6182   else {
6183     return EXTR_BAD_PATH_SHAPE;
6184   }
6185
6186   return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
6187                           theHasRefPoint, theRefPoint, theMakeGroups);
6188 }
6189
6190
6191 //=======================================================================
6192 //function : ExtrusionAlongTrack
6193 //purpose  :
6194 //=======================================================================
6195 SMESH_MeshEditor::Extrusion_Error
6196 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet     theElements[2],
6197                                        SMESH_Mesh*          theTrack,
6198                                        const SMDS_MeshNode* theN1,
6199                                        const bool           theHasAngles,
6200                                        list<double>&        theAngles,
6201                                        const bool           theLinearVariation,
6202                                        const bool           theHasRefPoint,
6203                                        const gp_Pnt&        theRefPoint,
6204                                        const bool           theMakeGroups)
6205 {
6206   myLastCreatedElems.Clear();
6207   myLastCreatedNodes.Clear();
6208
6209   int aNbE;
6210   std::list<double> aPrms;
6211   TIDSortedElemSet::iterator itElem;
6212
6213   gp_XYZ aGC;
6214   TopoDS_Edge aTrackEdge;
6215   TopoDS_Vertex aV1, aV2;
6216
6217   SMDS_ElemIteratorPtr aItE;
6218   SMDS_NodeIteratorPtr aItN;
6219   SMDSAbs_ElementType aTypeE;
6220
6221   TNodeOfNodeListMap mapNewNodes;
6222
6223   // 1. Check data
6224   aNbE = theElements[0].size() + theElements[1].size();
6225   // nothing to do
6226   if ( !aNbE )
6227     return EXTR_NO_ELEMENTS;
6228
6229   // 1.1 Track Pattern
6230   ASSERT( theTrack );
6231
6232   SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS();
6233
6234   aItE = pMeshDS->elementsIterator();
6235   while ( aItE->more() ) {
6236     const SMDS_MeshElement* pE = aItE->next();
6237     aTypeE = pE->GetType();
6238     // Pattern must contain links only
6239     if ( aTypeE != SMDSAbs_Edge )
6240       return EXTR_PATH_NOT_EDGE;
6241   }
6242
6243   list<SMESH_MeshEditor_PathPoint> fullList;
6244
6245   const TopoDS_Shape& aS = theTrack->GetShapeToMesh();
6246
6247   if ( !theTrack->HasShapeToMesh() ) {
6248     //Mesh without shape
6249     const SMDS_MeshNode* currentNode = NULL;
6250     const SMDS_MeshNode* prevNode = theN1;
6251     std::vector<const SMDS_MeshNode*> aNodesList;
6252     aNodesList.push_back(theN1);
6253     int nbEdges = 0, conn=0;
6254     const SMDS_MeshElement* prevElem = NULL;
6255     const SMDS_MeshElement* currentElem = NULL;
6256     int totalNbEdges = theTrack->NbEdges();
6257     SMDS_ElemIteratorPtr nIt;
6258
6259     //check start node
6260     if( !theTrack->GetMeshDS()->Contains(theN1) ) {
6261       return EXTR_BAD_STARTING_NODE;
6262     }
6263
6264     conn = nbEdgeConnectivity(theN1);
6265     if( conn != 1 )
6266       return EXTR_PATH_NOT_EDGE;
6267
6268     aItE = theN1->GetInverseElementIterator();
6269     prevElem = aItE->next();
6270     currentElem = prevElem;
6271     //Get all nodes
6272     if(totalNbEdges == 1 ) {
6273       nIt = currentElem->nodesIterator();
6274       currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6275       if(currentNode == prevNode)
6276         currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6277       aNodesList.push_back(currentNode);
6278     } else {
6279       nIt = currentElem->nodesIterator();
6280       while( nIt->more() ) {
6281         currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6282         if(currentNode == prevNode)
6283           currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6284         aNodesList.push_back(currentNode);
6285
6286         //case of the closed mesh
6287         if(currentNode == theN1) {
6288           nbEdges++;
6289           break;
6290         }
6291
6292         conn = nbEdgeConnectivity(currentNode);
6293         if(conn > 2) {
6294           return EXTR_PATH_NOT_EDGE;
6295         }else if( conn == 1 && nbEdges > 0 ) {
6296           //End of the path
6297           nbEdges++;
6298           break;
6299         }else {
6300           prevNode = currentNode;
6301           aItE = currentNode->GetInverseElementIterator();
6302           currentElem = aItE->next();
6303           if( currentElem  == prevElem)
6304             currentElem = aItE->next();
6305           nIt = currentElem->nodesIterator();
6306           prevElem = currentElem;
6307           nbEdges++;
6308         }
6309       }
6310     }
6311
6312     if(nbEdges != totalNbEdges)
6313       return EXTR_PATH_NOT_EDGE;
6314
6315     TopTools_SequenceOfShape Edges;
6316     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6317     int startNid = theN1->GetID();
6318     for ( size_t i = 1; i < aNodesList.size(); i++ )
6319     {
6320       gp_Pnt     p1 = SMESH_TNodeXYZ( aNodesList[i-1] );
6321       gp_Pnt     p2 = SMESH_TNodeXYZ( aNodesList[i] );
6322       TopoDS_Edge e = BRepBuilderAPI_MakeEdge( p1, p2 );
6323       list<SMESH_MeshEditor_PathPoint> LPP;
6324       aPrms.clear();
6325       MakeEdgePathPoints(aPrms, e, (aNodesList[i-1]->GetID()==startNid), LPP);
6326       LLPPs.push_back(LPP);
6327       if ( aNodesList[i-1]->GetID() == startNid ) startNid = aNodesList[i  ]->GetID();
6328       else                                        startNid = aNodesList[i-1]->GetID();
6329     }
6330
6331     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6332     list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
6333     list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
6334     for(; itPP!=firstList.end(); itPP++) {
6335       fullList.push_back( *itPP );
6336     }
6337
6338     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6339     SMESH_MeshEditor_PathPoint PP2;
6340     fullList.pop_back();
6341     itLLPP++;
6342     for(; itLLPP!=LLPPs.end(); itLLPP++) {
6343       list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
6344       itPP = currList.begin();
6345       PP2 = currList.front();
6346       gp_Dir D1 = PP1.Tangent();
6347       gp_Dir D2 = PP2.Tangent();
6348       gp_Dir Dnew( 0.5 * ( D1.XYZ() + D2.XYZ() ));
6349       PP1.SetTangent(Dnew);
6350       fullList.push_back(PP1);
6351       itPP++;
6352       for(; itPP!=currList.end(); itPP++) {
6353         fullList.push_back( *itPP );
6354       }
6355       PP1 = fullList.back();
6356       fullList.pop_back();
6357     }
6358     fullList.push_back(PP1);
6359
6360   } // Sub-shape for the Pattern must be an Edge or Wire
6361   else if ( aS.ShapeType() == TopAbs_EDGE )
6362   {
6363     aTrackEdge = TopoDS::Edge( aS );
6364     // the Edge must not be degenerated
6365     if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
6366       return EXTR_BAD_PATH_SHAPE;
6367     TopExp::Vertices( aTrackEdge, aV1, aV2 );
6368     const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6369     const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6370     // starting node must be aN1 or aN2
6371     if ( !( aN1 == theN1 || aN2 == theN1 ) )
6372       return EXTR_BAD_STARTING_NODE;
6373     aItN = pMeshDS->nodesIterator();
6374     while ( aItN->more() ) {
6375       const SMDS_MeshNode* pNode = aItN->next();
6376       if( pNode==aN1 || pNode==aN2 ) continue;
6377       const SMDS_EdgePosition* pEPos =
6378         static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6379       double aT = pEPos->GetUParameter();
6380       aPrms.push_back( aT );
6381     }
6382     //Extrusion_Error err =
6383     MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
6384   }
6385   else if( aS.ShapeType() == TopAbs_WIRE ) {
6386     list< SMESH_subMesh* > LSM;
6387     TopTools_SequenceOfShape Edges;
6388     TopExp_Explorer eExp(aS, TopAbs_EDGE);
6389     for(; eExp.More(); eExp.Next()) {
6390       TopoDS_Edge E = TopoDS::Edge( eExp.Current() );
6391       if( SMESH_Algo::isDegenerated(E) ) continue;
6392       SMESH_subMesh* SM = theTrack->GetSubMesh(E);
6393       if(SM) {
6394         LSM.push_back(SM);
6395         Edges.Append(E);
6396       }
6397     }
6398     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6399     TopoDS_Vertex aVprev;
6400     TColStd_MapOfInteger UsedNums;
6401     int NbEdges = Edges.Length();
6402     int i = 1;
6403     for(; i<=NbEdges; i++) {
6404       int k = 0;
6405       list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
6406       for(; itLSM!=LSM.end(); itLSM++) {
6407         k++;
6408         if(UsedNums.Contains(k)) continue;
6409         aTrackEdge = TopoDS::Edge( Edges.Value(k) );
6410         SMESH_subMesh* locTrack = *itLSM;
6411         SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
6412         TopExp::Vertices( aTrackEdge, aV1, aV2 );
6413         bool aN1isOK = false, aN2isOK = false;
6414         if ( aVprev.IsNull() ) {
6415           // if previous vertex is not yet defined, it means that we in the beginning of wire
6416           // and we have to find initial vertex corresponding to starting node theN1
6417           const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6418           const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6419           // starting node must be aN1 or aN2
6420           aN1isOK = ( aN1 && aN1 == theN1 );
6421           aN2isOK = ( aN2 && aN2 == theN1 );
6422         }
6423         else {
6424           // we have specified ending vertex of the previous edge on the previous iteration
6425           // and we have just to check that it corresponds to any vertex in current segment
6426           aN1isOK = aVprev.IsSame( aV1 );
6427           aN2isOK = aVprev.IsSame( aV2 );
6428         }
6429         if ( !aN1isOK && !aN2isOK ) continue;
6430         // 2. Collect parameters on the track edge
6431         aPrms.clear();
6432         aItN = locMeshDS->GetNodes();
6433         while ( aItN->more() ) {
6434           const SMDS_MeshNode*     pNode = aItN->next();
6435           const SMDS_EdgePosition* pEPos =
6436             static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6437           double aT = pEPos->GetUParameter();
6438           aPrms.push_back( aT );
6439         }
6440         list<SMESH_MeshEditor_PathPoint> LPP;
6441         //Extrusion_Error err =
6442         MakeEdgePathPoints(aPrms, aTrackEdge, aN1isOK, LPP);
6443         LLPPs.push_back(LPP);
6444         UsedNums.Add(k);
6445         // update startN for search following egde
6446         if ( aN1isOK ) aVprev = aV2;
6447         else           aVprev = aV1;
6448         break;
6449       }
6450     }
6451     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6452     list<SMESH_MeshEditor_PathPoint>& firstList = *itLLPP;
6453     fullList.splice( fullList.end(), firstList );
6454
6455     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6456     fullList.pop_back();
6457     itLLPP++;
6458     for(; itLLPP!=LLPPs.end(); itLLPP++) {
6459       list<SMESH_MeshEditor_PathPoint>& currList = *itLLPP;
6460       SMESH_MeshEditor_PathPoint PP2 = currList.front();
6461       gp_Dir D1 = PP1.Tangent();
6462       gp_Dir D2 = PP2.Tangent();
6463       gp_Dir Dnew( D1.XYZ() + D2.XYZ() );
6464       PP1.SetTangent(Dnew);
6465       fullList.push_back(PP1);
6466       fullList.splice( fullList.end(), currList, ++currList.begin(), currList.end() );
6467       PP1 = fullList.back();
6468       fullList.pop_back();
6469     }
6470     // if wire not closed
6471     fullList.push_back(PP1);
6472     // else ???
6473   }
6474   else {
6475     return EXTR_BAD_PATH_SHAPE;
6476   }
6477
6478   return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
6479                           theHasRefPoint, theRefPoint, theMakeGroups);
6480 }
6481
6482
6483 //=======================================================================
6484 //function : MakeEdgePathPoints
6485 //purpose  : auxilary for ExtrusionAlongTrack
6486 //=======================================================================
6487 SMESH_MeshEditor::Extrusion_Error
6488 SMESH_MeshEditor::MakeEdgePathPoints(std::list<double>&                aPrms,
6489                                      const TopoDS_Edge&                aTrackEdge,
6490                                      bool                              FirstIsStart,
6491                                      list<SMESH_MeshEditor_PathPoint>& LPP)
6492 {
6493   Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2;
6494   aTolVec=1.e-7;
6495   aTolVec2=aTolVec*aTolVec;
6496   double aT1, aT2;
6497   TopoDS_Vertex aV1, aV2;
6498   TopExp::Vertices( aTrackEdge, aV1, aV2 );
6499   aT1=BRep_Tool::Parameter( aV1, aTrackEdge );
6500   aT2=BRep_Tool::Parameter( aV2, aTrackEdge );
6501   // 2. Collect parameters on the track edge
6502   aPrms.push_front( aT1 );
6503   aPrms.push_back( aT2 );
6504   // sort parameters
6505   aPrms.sort();
6506   if( FirstIsStart ) {
6507     if ( aT1 > aT2 ) {
6508       aPrms.reverse();
6509     }
6510   }
6511   else {
6512     if ( aT2 > aT1 ) {
6513       aPrms.reverse();
6514     }
6515   }
6516   // 3. Path Points
6517   SMESH_MeshEditor_PathPoint aPP;
6518   Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 );
6519   std::list<double>::iterator aItD = aPrms.begin();
6520   for(; aItD != aPrms.end(); ++aItD) {
6521     double aT = *aItD;
6522     gp_Pnt aP3D;
6523     gp_Vec aVec;
6524     aC3D->D1( aT, aP3D, aVec );
6525     aL2 = aVec.SquareMagnitude();
6526     if ( aL2 < aTolVec2 )
6527       return EXTR_CANT_GET_TANGENT;
6528     gp_Dir aTgt( FirstIsStart ? aVec : -aVec );
6529     aPP.SetPnt( aP3D );
6530     aPP.SetTangent( aTgt );
6531     aPP.SetParameter( aT );
6532     LPP.push_back(aPP);
6533   }
6534   return EXTR_OK;
6535 }
6536
6537
6538 //=======================================================================
6539 //function : MakeExtrElements
6540 //purpose  : auxilary for ExtrusionAlongTrack
6541 //=======================================================================
6542 SMESH_MeshEditor::Extrusion_Error
6543 SMESH_MeshEditor::MakeExtrElements(TIDSortedElemSet                  theElemSets[2],
6544                                    list<SMESH_MeshEditor_PathPoint>& fullList,
6545                                    const bool                        theHasAngles,
6546                                    list<double>&                     theAngles,
6547                                    const bool                        theLinearVariation,
6548                                    const bool                        theHasRefPoint,
6549                                    const gp_Pnt&                     theRefPoint,
6550                                    const bool                        theMakeGroups)
6551 {
6552   const int aNbTP = fullList.size();
6553
6554   // Angles
6555   if( theHasAngles && !theAngles.empty() && theLinearVariation )
6556     LinearAngleVariation(aNbTP-1, theAngles);
6557
6558   // fill vector of path points with angles
6559   vector<SMESH_MeshEditor_PathPoint> aPPs;
6560   list<SMESH_MeshEditor_PathPoint>::iterator itPP = fullList.begin();
6561   list<double>::iterator                 itAngles = theAngles.begin();
6562   aPPs.push_back( *itPP++ );
6563   for( ; itPP != fullList.end(); itPP++) {
6564     aPPs.push_back( *itPP );
6565     if ( theHasAngles && itAngles != theAngles.end() )
6566       aPPs.back().SetAngle( *itAngles++ );
6567   }
6568
6569   TNodeOfNodeListMap   mapNewNodes;
6570   TElemOfVecOfNnlmiMap mapElemNewNodes;
6571   TTElemOfElemListMap  newElemsMap;
6572   TIDSortedElemSet::iterator itElem;
6573   // source elements for each generated one
6574   SMESH_SequenceOfElemPtr srcElems, srcNodes;
6575
6576   // 3. Center of rotation aV0
6577   gp_Pnt aV0 = theRefPoint;
6578   if ( !theHasRefPoint )
6579   {
6580     gp_XYZ aGC( 0.,0.,0. );
6581     TIDSortedElemSet newNodes;
6582
6583     for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6584     {
6585       TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6586       itElem = theElements.begin();
6587       for ( ; itElem != theElements.end(); itElem++ )
6588       {
6589         const SMDS_MeshElement* elem = *itElem;
6590         SMDS_ElemIteratorPtr     itN = elem->nodesIterator();
6591         while ( itN->more() ) {
6592           const SMDS_MeshElement* node = itN->next();
6593           if ( newNodes.insert( node ).second )
6594             aGC += SMESH_TNodeXYZ( node );
6595         }
6596       }
6597     }
6598     aGC /= newNodes.size();
6599     aV0.SetXYZ( aGC );
6600   } // if (!theHasRefPoint) {
6601
6602   // 4. Processing the elements
6603   SMESHDS_Mesh* aMesh = GetMeshDS();
6604   list<const SMDS_MeshNode*> emptyList;
6605
6606   setElemsFirst( theElemSets );
6607   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6608   {
6609     TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6610     for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ )
6611     {
6612       const SMDS_MeshElement* elem = *itElem;
6613
6614       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
6615       newNodesItVec.reserve( elem->NbNodes() );
6616
6617       // loop on elem nodes
6618       int nodeIndex = -1;
6619       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6620       while ( itN->more() )
6621       {
6622         ++nodeIndex;
6623         // check if a node has been already processed
6624         const SMDS_MeshNode* node = cast2Node( itN->next() );
6625         TNodeOfNodeListMap::iterator nIt = mapNewNodes.insert( make_pair( node, emptyList )).first;
6626         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
6627         if ( listNewNodes.empty() )
6628         {
6629           // make new nodes
6630           Standard_Real aAngle1x, aAngleT1T0, aTolAng;
6631           gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
6632           gp_Ax1 anAx1, anAxT1T0;
6633           gp_Dir aDT1x, aDT0x, aDT1T0;
6634
6635           aTolAng=1.e-4;
6636
6637           aV0x = aV0;
6638           aPN0 = SMESH_TNodeXYZ( node );
6639
6640           const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
6641           aP0x = aPP0.Pnt();
6642           aDT0x= aPP0.Tangent();
6643
6644           for ( int j = 1; j < aNbTP; ++j ) {
6645             const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
6646             aP1x     = aPP1.Pnt();
6647             aDT1x    = aPP1.Tangent();
6648             aAngle1x = aPP1.Angle();
6649
6650             gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
6651             // Translation
6652             gp_Vec aV01x( aP0x, aP1x );
6653             aTrsf.SetTranslation( aV01x );
6654
6655             // traslated point
6656             aV1x = aV0x.Transformed( aTrsf );
6657             aPN1 = aPN0.Transformed( aTrsf );
6658
6659             // rotation 1 [ T1,T0 ]
6660             aAngleT1T0=-aDT1x.Angle( aDT0x );
6661             if (fabs(aAngleT1T0) > aTolAng)
6662             {
6663               aDT1T0=aDT1x^aDT0x;
6664               anAxT1T0.SetLocation( aV1x );
6665               anAxT1T0.SetDirection( aDT1T0 );
6666               aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
6667
6668               aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
6669             }
6670
6671             // rotation 2
6672             if ( theHasAngles ) {
6673               anAx1.SetLocation( aV1x );
6674               anAx1.SetDirection( aDT1x );
6675               aTrsfRot.SetRotation( anAx1, aAngle1x );
6676
6677               aPN1 = aPN1.Transformed( aTrsfRot );
6678             }
6679
6680             // make new node
6681             if ( elem->IsQuadratic() && !elem->IsMediumNode(node) )
6682             {
6683               // create additional node
6684               gp_XYZ midP = 0.5 * ( aPN1.XYZ() + aPN0.XYZ() );
6685               const SMDS_MeshNode* newNode = aMesh->AddNode( midP.X(), midP.Y(), midP.Z() );
6686               myLastCreatedNodes.Append(newNode);
6687               srcNodes.Append( node );
6688               listNewNodes.push_back( newNode );
6689             }
6690             const SMDS_MeshNode* newNode = aMesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
6691             myLastCreatedNodes.Append(newNode);
6692             srcNodes.Append( node );
6693             listNewNodes.push_back( newNode );
6694
6695             aPN0 = aPN1;
6696             aP0x = aP1x;
6697             aV0x = aV1x;
6698             aDT0x = aDT1x;
6699           }
6700         }
6701         else if( elem->IsQuadratic() && !elem->IsMediumNode(node) )
6702         {
6703           // if current elem is quadratic and current node is not medium
6704           // we have to check - may be it is needed to insert additional nodes
6705           list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
6706           if ((int) listNewNodes.size() == aNbTP-1 )
6707           {
6708             vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
6709             gp_XYZ P(node->X(), node->Y(), node->Z());
6710             list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
6711             int i;
6712             for(i=0; i<aNbTP-1; i++) {
6713               const SMDS_MeshNode* N = *it;
6714               double x = ( N->X() + P.X() )/2.;
6715               double y = ( N->Y() + P.Y() )/2.;
6716               double z = ( N->Z() + P.Z() )/2.;
6717               const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
6718               srcNodes.Append( node );
6719               myLastCreatedNodes.Append(newN);
6720               aNodes[2*i] = newN;
6721               aNodes[2*i+1] = N;
6722               P = gp_XYZ(N->X(),N->Y(),N->Z());
6723             }
6724             listNewNodes.clear();
6725             for(i=0; i<2*(aNbTP-1); i++) {
6726               listNewNodes.push_back(aNodes[i]);
6727             }
6728           }
6729         }
6730
6731         newNodesItVec.push_back( nIt );
6732       }
6733
6734       // make new elements
6735       sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
6736     }
6737   }
6738
6739   makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], aNbTP-1, srcElems );
6740
6741   if ( theMakeGroups )
6742     generateGroups( srcNodes, srcElems, "extruded");
6743
6744   return EXTR_OK;
6745 }
6746
6747
6748 //=======================================================================
6749 //function : LinearAngleVariation
6750 //purpose  : spread values over nbSteps
6751 //=======================================================================
6752
6753 void SMESH_MeshEditor::LinearAngleVariation(const int     nbSteps,
6754                                             list<double>& Angles)
6755 {
6756   int nbAngles = Angles.size();
6757   if( nbSteps > nbAngles && nbAngles > 0 )
6758   {
6759     vector<double> theAngles(nbAngles);
6760     theAngles.assign( Angles.begin(), Angles.end() );
6761
6762     list<double> res;
6763     double rAn2St = double( nbAngles ) / double( nbSteps );
6764     double angPrev = 0, angle;
6765     for ( int iSt = 0; iSt < nbSteps; ++iSt )
6766     {
6767       double angCur = rAn2St * ( iSt+1 );
6768       double angCurFloor  = floor( angCur );
6769       double angPrevFloor = floor( angPrev );
6770       if ( angPrevFloor == angCurFloor )
6771         angle = rAn2St * theAngles[ int( angCurFloor ) ];
6772       else {
6773         int iP = int( angPrevFloor );
6774         double angPrevCeil = ceil(angPrev);
6775         angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6776
6777         int iC = int( angCurFloor );
6778         if ( iC < nbAngles )
6779           angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6780
6781         iP = int( angPrevCeil );
6782         while ( iC-- > iP )
6783           angle += theAngles[ iC ];
6784       }
6785       res.push_back(angle);
6786       angPrev = angCur;
6787     }
6788     Angles.swap( res );
6789   }
6790 }
6791
6792
6793 //================================================================================
6794 /*!
6795  * \brief Move or copy theElements applying theTrsf to their nodes
6796  *  \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6797  *  \param theTrsf - transformation to apply
6798  *  \param theCopy - if true, create translated copies of theElems
6799  *  \param theMakeGroups - if true and theCopy, create translated groups
6800  *  \param theTargetMesh - mesh to copy translated elements into
6801  *  \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6802  */
6803 //================================================================================
6804
6805 SMESH_MeshEditor::PGroupIDs
6806 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6807                              const gp_Trsf&     theTrsf,
6808                              const bool         theCopy,
6809                              const bool         theMakeGroups,
6810                              SMESH_Mesh*        theTargetMesh)
6811 {
6812   myLastCreatedElems.Clear();
6813   myLastCreatedNodes.Clear();
6814
6815   bool needReverse = false;
6816   string groupPostfix;
6817   switch ( theTrsf.Form() ) {
6818   case gp_PntMirror:
6819     needReverse = true;
6820     groupPostfix = "mirrored";
6821     break;
6822   case gp_Ax1Mirror:
6823     groupPostfix = "mirrored";
6824     break;
6825   case gp_Ax2Mirror:
6826     needReverse = true;
6827     groupPostfix = "mirrored";
6828     break;
6829   case gp_Rotation:
6830     groupPostfix = "rotated";
6831     break;
6832   case gp_Translation:
6833     groupPostfix = "translated";
6834     break;
6835   case gp_Scale:
6836     groupPostfix = "scaled";
6837     break;
6838   case gp_CompoundTrsf: // different scale by axis
6839     groupPostfix = "scaled";
6840     break;
6841   default:
6842     needReverse = false;
6843     groupPostfix = "transformed";
6844   }
6845
6846   SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6847   SMESHDS_Mesh* aMesh    = GetMeshDS();
6848
6849   SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6850   SMESH_MeshEditor* editor = theTargetMesh ? & targetMeshEditor : theCopy ? this : 0;
6851   SMESH_MeshEditor::ElemFeatures elemType;
6852
6853   // map old node to new one
6854   TNodeNodeMap nodeMap;
6855
6856   // elements sharing moved nodes; those of them which have all
6857   // nodes mirrored but are not in theElems are to be reversed
6858   TIDSortedElemSet inverseElemSet;
6859
6860   // source elements for each generated one
6861   SMESH_SequenceOfElemPtr srcElems, srcNodes;
6862
6863   // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6864   TIDSortedElemSet orphanNode;
6865
6866   if ( theElems.empty() ) // transform the whole mesh
6867   {
6868     // add all elements
6869     SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6870     while ( eIt->more() ) theElems.insert( eIt->next() );
6871     // add orphan nodes
6872     SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6873     while ( nIt->more() )
6874     {
6875       const SMDS_MeshNode* node = nIt->next();
6876       if ( node->NbInverseElements() == 0)
6877         orphanNode.insert( node );
6878     }
6879   }
6880
6881   // loop on elements to transform nodes : first orphan nodes then elems
6882   TIDSortedElemSet::iterator itElem;
6883   TIDSortedElemSet *elements[] = { &orphanNode, &theElems };
6884   for (int i=0; i<2; i++)
6885     for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ )
6886     {
6887       const SMDS_MeshElement* elem = *itElem;
6888       if ( !elem )
6889         continue;
6890
6891       // loop on elem nodes
6892       double coord[3];
6893       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6894       while ( itN->more() )
6895       {
6896         const SMDS_MeshNode* node = cast2Node( itN->next() );
6897         // check if a node has been already transformed
6898         pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6899           nodeMap.insert( make_pair ( node, node ));
6900         if ( !n2n_isnew.second )
6901           continue;
6902
6903         node->GetXYZ( coord );
6904         theTrsf.Transforms( coord[0], coord[1], coord[2] );
6905         if ( theTargetMesh ) {
6906           const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6907           n2n_isnew.first->second = newNode;
6908           myLastCreatedNodes.Append(newNode);
6909           srcNodes.Append( node );
6910         }
6911         else if ( theCopy ) {
6912           const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6913           n2n_isnew.first->second = newNode;
6914           myLastCreatedNodes.Append(newNode);
6915           srcNodes.Append( node );
6916         }
6917         else {
6918           aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6919           // node position on shape becomes invalid
6920           const_cast< SMDS_MeshNode* > ( node )->SetPosition
6921             ( SMDS_SpacePosition::originSpacePosition() );
6922         }
6923
6924         // keep inverse elements
6925         if ( !theCopy && !theTargetMesh && needReverse ) {
6926           SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6927           while ( invElemIt->more() ) {
6928             const SMDS_MeshElement* iel = invElemIt->next();
6929             inverseElemSet.insert( iel );
6930           }
6931         }
6932       }
6933     } // loop on elems in { &orphanNode, &theElems };
6934
6935   // either create new elements or reverse mirrored ones
6936   if ( !theCopy && !needReverse && !theTargetMesh )
6937     return PGroupIDs();
6938
6939   theElems.insert( inverseElemSet.begin(),inverseElemSet.end() );
6940
6941   // Replicate or reverse elements
6942
6943   std::vector<int> iForw;
6944   vector<const SMDS_MeshNode*> nodes;
6945   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6946   {
6947     const SMDS_MeshElement* elem = *itElem;
6948     if ( !elem ) continue;
6949
6950     SMDSAbs_GeometryType geomType = elem->GetGeomType();
6951     size_t               nbNodes  = elem->NbNodes();
6952     if ( geomType == SMDSGeom_NONE ) continue; // node
6953
6954     nodes.resize( nbNodes );
6955
6956     if ( geomType == SMDSGeom_POLYHEDRA )  // ------------------ polyhedral volume
6957     {
6958       const SMDS_VtkVolume* aPolyedre = dynamic_cast<const SMDS_VtkVolume*>( elem );
6959       if (!aPolyedre)
6960         continue;
6961       nodes.clear();
6962       bool allTransformed = true;
6963       int nbFaces = aPolyedre->NbFaces();
6964       for (int iface = 1; iface <= nbFaces && allTransformed; iface++)
6965       {
6966         int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6967         for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++)
6968         {
6969           const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6970           TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6971           if ( nodeMapIt == nodeMap.end() )
6972             allTransformed = false; // not all nodes transformed
6973           else
6974             nodes.push_back((*nodeMapIt).second);
6975         }
6976         if ( needReverse && allTransformed )
6977           std::reverse( nodes.end() - nbFaceNodes, nodes.end() );
6978       }
6979       if ( !allTransformed )
6980         continue; // not all nodes transformed
6981     }
6982     else // ----------------------- the rest element types
6983     {
6984       while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6985       const vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType(), nbNodes );
6986       const vector<int>&    i = needReverse ? iRev : iForw;
6987
6988       // find transformed nodes
6989       size_t iNode = 0;
6990       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6991       while ( itN->more() ) {
6992         const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
6993         TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6994         if ( nodeMapIt == nodeMap.end() )
6995           break; // not all nodes transformed
6996         nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
6997       }
6998       if ( iNode != nbNodes )
6999         continue; // not all nodes transformed
7000     }
7001
7002     if ( editor ) {
7003       // copy in this or a new mesh
7004       if ( editor->AddElement( nodes, elemType.Init( elem, /*basicOnly=*/false )))
7005         srcElems.Append( elem );
7006     }
7007     else {
7008       // reverse element as it was reversed by transformation
7009       if ( nbNodes > 2 )
7010         aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
7011     }
7012
7013   } // loop on elements
7014
7015   if ( editor && editor != this )
7016     myLastCreatedElems = editor->myLastCreatedElems;
7017
7018   PGroupIDs newGroupIDs;
7019
7020   if ( ( theMakeGroups && theCopy ) ||
7021        ( theMakeGroups && theTargetMesh ) )
7022     newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh, false );
7023
7024   return newGroupIDs;
7025 }
7026
7027 //=======================================================================
7028 /*!
7029  * \brief Create groups of elements made during transformation
7030  *  \param nodeGens - nodes making corresponding myLastCreatedNodes
7031  *  \param elemGens - elements making corresponding myLastCreatedElems
7032  *  \param postfix - to append to names of new groups
7033  *  \param targetMesh - mesh to create groups in
7034  *  \param topPresent - is there "top" elements that are created by sweeping
7035  */
7036 //=======================================================================
7037
7038 SMESH_MeshEditor::PGroupIDs
7039 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
7040                                  const SMESH_SequenceOfElemPtr& elemGens,
7041                                  const std::string&             postfix,
7042                                  SMESH_Mesh*                    targetMesh,
7043                                  const bool                     topPresent)
7044 {
7045   PGroupIDs newGroupIDs( new list<int> );
7046   SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
7047
7048   // Sort existing groups by types and collect their names
7049
7050   // containers to store an old group and generated new ones;
7051   // 1st new group is for result elems of different type than a source one;
7052   // 2nd new group is for same type result elems ("top" group at extrusion)
7053   using boost::tuple;
7054   using boost::make_tuple;
7055   typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
7056   vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
7057   vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
7058   // group names
7059   set< string > groupNames;
7060
7061   SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
7062   if ( !groupIt->more() ) return newGroupIDs;
7063
7064   int newGroupID = mesh->GetGroupIds().back()+1;
7065   while ( groupIt->more() )
7066   {
7067     SMESH_Group * group = groupIt->next();
7068     if ( !group ) continue;
7069     SMESHDS_GroupBase* groupDS = group->GetGroupDS();
7070     if ( !groupDS || groupDS->IsEmpty() ) continue;
7071     groupNames.insert    ( group->GetName() );
7072     groupDS->SetStoreName( group->GetName() );
7073     const SMDSAbs_ElementType type = groupDS->GetType();
7074     SMESHDS_Group* newGroup    = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
7075     SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
7076     groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
7077     orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
7078   }
7079
7080   // Loop on nodes and elements to add them in new groups
7081
7082   vector< const SMDS_MeshElement* > resultElems;
7083   for ( int isNodes = 0; isNodes < 2; ++isNodes )
7084   {
7085     const SMESH_SequenceOfElemPtr& gens  = isNodes ? nodeGens : elemGens;
7086     const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
7087     if ( gens.Length() != elems.Length() )
7088       throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
7089
7090     // loop on created elements
7091     for (int iElem = 1; iElem <= elems.Length(); ++iElem )
7092     {
7093       const SMDS_MeshElement* sourceElem = gens( iElem );
7094       if ( !sourceElem ) {
7095         MESSAGE("generateGroups(): NULL source element");
7096         continue;
7097       }
7098       list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
7099       if ( groupsOldNew.empty() ) { // no groups of this type at all
7100         while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
7101           ++iElem; // skip all elements made by sourceElem
7102         continue;
7103       }
7104       // collect all elements made by the iElem-th sourceElem
7105       resultElems.clear();
7106       if ( const SMDS_MeshElement* resElem = elems( iElem ))
7107         if ( resElem != sourceElem )
7108           resultElems.push_back( resElem );
7109       while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
7110         if ( const SMDS_MeshElement* resElem = elems( ++iElem ))
7111           if ( resElem != sourceElem )
7112             resultElems.push_back( resElem );
7113
7114       const SMDS_MeshElement* topElem = 0;
7115       if ( isNodes ) // there must be a top element
7116       {
7117         topElem = resultElems.back();
7118         resultElems.pop_back();
7119       }
7120       else
7121       {
7122         vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
7123         for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
7124           if ( (*resElemIt)->GetType() == sourceElem->GetType() )
7125           {
7126             topElem = *resElemIt;
7127             *resElemIt = 0; // erase *resElemIt
7128             break;
7129           }
7130       }
7131       // add resultElems to groups originted from ones the sourceElem belongs to
7132       list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
7133       for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
7134       {
7135         SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
7136         if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
7137         {
7138           // fill in a new group
7139           SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
7140           vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
7141           for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
7142             if ( *resElemIt )
7143               newGroup.Add( *resElemIt );
7144
7145           // fill a "top" group
7146           if ( topElem )
7147           {
7148             SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
7149             newTopGroup.Add( topElem );
7150          }
7151         }
7152       }
7153     } // loop on created elements
7154   }// loop on nodes and elements
7155
7156   // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
7157
7158   list<int> topGrouIds;
7159   for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
7160   {
7161     SMESHDS_GroupBase* oldGroupDS =   orderedOldNewGroups[i]->get<0>();
7162     SMESHDS_Group*   newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
7163                                       orderedOldNewGroups[i]->get<2>() };
7164     for ( int is2nd = 0; is2nd < 2; ++is2nd )
7165     {
7166       SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
7167       if ( newGroupDS->IsEmpty() )
7168       {
7169         mesh->GetMeshDS()->RemoveGroup( newGroupDS );
7170       }
7171       else
7172       {
7173         // set group type
7174         newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
7175
7176         // make a name
7177         const bool isTop = ( topPresent &&
7178                              newGroupDS->GetType() == oldGroupDS->GetType() &&
7179                              is2nd );
7180
7181         string name = oldGroupDS->GetStoreName();
7182         { // remove trailing whitespaces (issue 22599)
7183           size_t size = name.size();
7184           while ( size > 1 && isspace( name[ size-1 ]))
7185             --size;
7186           if ( size != name.size() )
7187           {
7188             name.resize( size );
7189             oldGroupDS->SetStoreName( name.c_str() );
7190           }
7191         }
7192         if ( !targetMesh ) {
7193           string suffix = ( isTop ? "top": postfix.c_str() );
7194           name += "_";
7195           name += suffix;
7196           int nb = 1;
7197           while ( !groupNames.insert( name ).second ) // name exists
7198             name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
7199         }
7200         else if ( isTop ) {
7201           name += "_top";
7202         }
7203         newGroupDS->SetStoreName( name.c_str() );
7204
7205         // make a SMESH_Groups
7206         mesh->AddGroup( newGroupDS );
7207         if ( isTop )
7208           topGrouIds.push_back( newGroupDS->GetID() );
7209         else
7210           newGroupIDs->push_back( newGroupDS->GetID() );
7211       }
7212     }
7213   }
7214   newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
7215
7216   return newGroupIDs;
7217 }
7218
7219 //================================================================================
7220 /*!
7221  *  * \brief Return list of group of nodes close to each other within theTolerance
7222  *  *        Search among theNodes or in the whole mesh if theNodes is empty using
7223  *  *        an Octree algorithm
7224  *  \param [in,out] theNodes - the nodes to treat
7225  *  \param [in]     theTolerance - the tolerance
7226  *  \param [out]    theGroupsOfNodes - the result groups of coincident nodes
7227  *  \param [in]     theSeparateCornersAndMedium - if \c true, in quadratic mesh puts 
7228  *         corner and medium nodes in separate groups
7229  */
7230 //================================================================================
7231
7232 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet &   theNodes,
7233                                             const double         theTolerance,
7234                                             TListOfListOfNodes & theGroupsOfNodes,
7235                                             bool                 theSeparateCornersAndMedium)
7236 {
7237   myLastCreatedElems.Clear();
7238   myLastCreatedNodes.Clear();
7239
7240   if ( myMesh->NbEdges  ( ORDER_QUADRATIC ) +
7241        myMesh->NbFaces  ( ORDER_QUADRATIC ) +
7242        myMesh->NbVolumes( ORDER_QUADRATIC ) == 0 )
7243     theSeparateCornersAndMedium = false;
7244
7245   TIDSortedNodeSet& corners = theNodes;
7246   TIDSortedNodeSet  medium;
7247
7248   if ( theNodes.empty() ) // get all nodes in the mesh
7249   {
7250     TIDSortedNodeSet* nodes[2] = { &corners, &medium };
7251     SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(/*idInceasingOrder=*/true);
7252     if ( theSeparateCornersAndMedium )
7253       while ( nIt->more() )
7254       {
7255         const SMDS_MeshNode* n = nIt->next();
7256         TIDSortedNodeSet* & nodeSet = nodes[ SMESH_MesherHelper::IsMedium( n )];
7257         nodeSet->insert( nodeSet->end(), n );
7258       }
7259     else
7260       while ( nIt->more() )
7261         theNodes.insert( theNodes.end(),nIt->next() );
7262   }
7263   else if ( theSeparateCornersAndMedium ) // separate corners from medium nodes
7264   {
7265     TIDSortedNodeSet::iterator nIt = corners.begin();
7266     while ( nIt != corners.end() )
7267       if ( SMESH_MesherHelper::IsMedium( *nIt ))
7268       {
7269         medium.insert( medium.end(), *nIt );
7270         corners.erase( nIt++ );
7271       }
7272       else
7273       {
7274         ++nIt;
7275       }
7276   }
7277
7278   if ( !corners.empty() )
7279     SMESH_OctreeNode::FindCoincidentNodes ( corners, &theGroupsOfNodes, theTolerance );
7280   if ( !medium.empty() )
7281     SMESH_OctreeNode::FindCoincidentNodes ( medium, &theGroupsOfNodes, theTolerance );
7282 }
7283
7284 //=======================================================================
7285 //function : SimplifyFace
7286 //purpose  : split a chain of nodes into several closed chains
7287 //=======================================================================
7288
7289 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
7290                                     vector<const SMDS_MeshNode *>&       poly_nodes,
7291                                     vector<int>&                         quantities) const
7292 {
7293   int nbNodes = faceNodes.size();
7294   while ( faceNodes[ 0 ] == faceNodes[ nbNodes-1 ] && nbNodes > 2 )
7295     --nbNodes;
7296   if ( nbNodes < 3 )
7297     return 0;
7298   size_t prevNbQuant = quantities.size();
7299
7300   vector< const SMDS_MeshNode* > simpleNodes; simpleNodes.reserve( nbNodes );
7301   map< const SMDS_MeshNode*, int > nodeIndices; // indices within simpleNodes
7302   map< const SMDS_MeshNode*, int >::iterator nInd;
7303
7304   nodeIndices.insert( make_pair( faceNodes[0], 0 ));
7305   simpleNodes.push_back( faceNodes[0] );
7306   for ( int iCur = 1; iCur < nbNodes; iCur++ )
7307   {
7308     if ( faceNodes[ iCur ] != simpleNodes.back() )
7309     {
7310       int index = simpleNodes.size();
7311       nInd = nodeIndices.insert( make_pair( faceNodes[ iCur ], index )).first;
7312       int prevIndex = nInd->second;
7313       if ( prevIndex < index )
7314       {
7315         // a sub-loop found
7316         int loopLen = index - prevIndex;
7317         if ( loopLen > 2 )
7318         {
7319           // store the sub-loop
7320           quantities.push_back( loopLen );
7321           for ( int i = prevIndex; i < index; i++ )
7322             poly_nodes.push_back( simpleNodes[ i ]);
7323         }
7324         simpleNodes.resize( prevIndex+1 );
7325       }
7326       else
7327       {
7328         simpleNodes.push_back( faceNodes[ iCur ]);
7329       }
7330     }
7331   }
7332
7333   if ( simpleNodes.size() > 2 )
7334   {
7335     quantities.push_back( simpleNodes.size() );
7336     poly_nodes.insert ( poly_nodes.end(), simpleNodes.begin(), simpleNodes.end() );
7337   }
7338
7339   return quantities.size() - prevNbQuant;
7340 }
7341
7342 //=======================================================================
7343 //function : MergeNodes
7344 //purpose  : In each group, the cdr of nodes are substituted by the first one
7345 //           in all elements.
7346 //=======================================================================
7347
7348 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes)
7349 {
7350   myLastCreatedElems.Clear();
7351   myLastCreatedNodes.Clear();
7352
7353   SMESHDS_Mesh* aMesh = GetMeshDS();
7354
7355   TNodeNodeMap nodeNodeMap; // node to replace - new node
7356   set<const SMDS_MeshElement*> elems; // all elements with changed nodes
7357   list< int > rmElemIds, rmNodeIds;
7358
7359   // Fill nodeNodeMap and elems
7360
7361   TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
7362   for ( ; grIt != theGroupsOfNodes.end(); grIt++ )
7363   {
7364     list<const SMDS_MeshNode*>& nodes = *grIt;
7365     list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
7366     const SMDS_MeshNode* nToKeep = *nIt;
7367     for ( ++nIt; nIt != nodes.end(); nIt++ )
7368     {
7369       const SMDS_MeshNode* nToRemove = *nIt;
7370       nodeNodeMap.insert( make_pair( nToRemove, nToKeep ));
7371       if ( nToRemove != nToKeep )
7372       {
7373         rmNodeIds.push_back( nToRemove->GetID() );
7374         AddToSameGroups( nToKeep, nToRemove, aMesh );
7375         // set _alwaysComputed to a sub-mesh of VERTEX to enable mesh computing
7376         // after MergeNodes() w/o creating node in place of merged ones.
7377         const SMDS_PositionPtr& pos = nToRemove->GetPosition();
7378         if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
7379           if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
7380             sm->SetIsAlwaysComputed( true );
7381       }
7382       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
7383       while ( invElemIt->more() ) {
7384         const SMDS_MeshElement* elem = invElemIt->next();
7385         elems.insert(elem);
7386       }
7387     }
7388   }
7389   // Change element nodes or remove an element
7390
7391   set<const SMDS_MeshNode*> nodeSet;
7392   vector< const SMDS_MeshNode*> curNodes, uniqueNodes;
7393   vector<int> iRepl;
7394   ElemFeatures elemType;
7395
7396   set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7397   for ( ; eIt != elems.end(); eIt++ )
7398   {
7399     const SMDS_MeshElement* elem = *eIt;
7400     const           int  nbNodes = elem->NbNodes();
7401     const           int aShapeId = FindShape( elem );
7402     SMDSAbs_EntityType    entity = elem->GetEntityType();
7403
7404     nodeSet.clear();
7405     curNodes.resize( nbNodes );
7406     uniqueNodes.resize( nbNodes );
7407     iRepl.resize( nbNodes );
7408     int iUnique = 0, iCur = 0, nbRepl = 0;
7409
7410     // get new seq of nodes
7411     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7412     while ( itN->more() )
7413     {
7414       const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7415
7416       TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7417       if ( nnIt != nodeNodeMap.end() ) { // n sticks
7418         n = (*nnIt).second;
7419         { ////////// BUG 0020185: begin
7420           bool stopRecur = false;
7421           set<const SMDS_MeshNode*> nodesRecur;
7422           nodesRecur.insert(n);
7423           while (!stopRecur) {
7424             TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( n );
7425             if ( nnIt_i != nodeNodeMap.end() ) { // n sticks
7426               n = (*nnIt_i).second;
7427               if (!nodesRecur.insert(n).second) {
7428                 // error: recursive dependency
7429                 stopRecur = true;
7430               }
7431             }
7432             else
7433               stopRecur = true;
7434           }
7435         } ////////// BUG 0020185: end
7436       }
7437       curNodes[ iCur ] = n;
7438       bool isUnique = nodeSet.insert( n ).second;
7439       if ( isUnique )
7440         uniqueNodes[ iUnique++ ] = n;
7441       else
7442         iRepl[ nbRepl++ ] = iCur;
7443       iCur++;
7444     }
7445
7446     // Analyse element topology after replacement
7447
7448     bool isOk = true;
7449     int nbUniqueNodes = nodeSet.size();
7450     if ( nbNodes != nbUniqueNodes ) // some nodes stick
7451     {
7452       if ( elem->IsPoly() ) // Polygons and Polyhedral volumes
7453       {
7454         if ( elem->GetType() == SMDSAbs_Face ) // Polygon
7455         {
7456           elemType.Init( elem );
7457           const bool isQuad = elemType.myIsQuad;
7458           if ( isQuad )
7459             SMDS_MeshCell::applyInterlace // interlace medium and corner nodes
7460               ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon, nbNodes ), curNodes );
7461
7462           // a polygon can divide into several elements
7463           vector<const SMDS_MeshNode *> polygons_nodes;
7464           vector<int> quantities;
7465           int nbNew = SimplifyFace( curNodes, polygons_nodes, quantities );
7466           if (nbNew > 0)
7467           {
7468             vector<const SMDS_MeshNode *> face_nodes;
7469             int inode = 0;
7470             for (int iface = 0; iface < nbNew; iface++)
7471             {
7472               int nbNewNodes = quantities[iface];
7473               face_nodes.assign( polygons_nodes.begin() + inode,
7474                                  polygons_nodes.begin() + inode + nbNewNodes );
7475               inode += nbNewNodes;
7476               if ( isQuad ) // check if a result elem is a valid quadratic polygon
7477               {
7478                 bool isValid = ( nbNewNodes % 2 == 0 );
7479                 for ( int i = 0; i < nbNewNodes && isValid; ++i )
7480                   isValid = ( elem->IsMediumNode( face_nodes[i]) == bool( i % 2 ));
7481                 elemType.SetQuad( isValid );
7482                 if ( isValid ) // put medium nodes after corners
7483                   SMDS_MeshCell::applyInterlaceRev
7484                     ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon,
7485                                                           nbNewNodes ), face_nodes );
7486               }
7487               elemType.SetPoly(( nbNewNodes / ( elemType.myIsQuad + 1 ) > 4 ));
7488
7489               SMDS_MeshElement* newElem = AddElement( face_nodes, elemType.SetID(-1));
7490               if ( aShapeId )
7491                 aMesh->SetMeshElementOnShape(newElem, aShapeId);
7492             }
7493           }
7494           rmElemIds.push_back(elem->GetID());
7495
7496         } // Polygon
7497
7498         else if ( elem->GetType() == SMDSAbs_Volume ) // Polyhedral volume
7499         {
7500           if ( nbUniqueNodes < 4 ) {
7501             rmElemIds.push_back(elem->GetID());
7502           }
7503           else {
7504             // each face has to be analyzed in order to check volume validity
7505             const SMDS_VtkVolume* aPolyedre = dynamic_cast<const SMDS_VtkVolume*>( elem );
7506             if ( aPolyedre )
7507             {
7508               int nbFaces = aPolyedre->NbFaces();
7509
7510               vector<const SMDS_MeshNode *> poly_nodes;
7511               vector<int>                   quantities;
7512               vector<const SMDS_MeshNode *> faceNodes;
7513
7514               for (int iface = 1; iface <= nbFaces; iface++)
7515               {
7516                 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7517                 faceNodes.resize( nbFaceNodes );
7518                 for (int inode = 1; inode <= nbFaceNodes; inode++)
7519                 {
7520                   const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7521                   TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7522                   if ( nnIt != nodeNodeMap.end() ) // faceNode sticks
7523                     faceNode = (*nnIt).second;
7524                   faceNodes[inode - 1] = faceNode;
7525                 }
7526                 SimplifyFace(faceNodes, poly_nodes, quantities);
7527               }
7528
7529               if ( quantities.size() > 3 ) {
7530                 // TODO: remove coincident faces
7531               }
7532
7533               if ( quantities.size() > 3 )
7534               {
7535                 const SMDS_MeshElement* newElem =
7536                   aMesh->AddPolyhedralVolume( poly_nodes, quantities );
7537                 myLastCreatedElems.Append( newElem );
7538                 if ( aShapeId && newElem )
7539                   aMesh->SetMeshElementOnShape( newElem, aShapeId );
7540                 rmElemIds.push_back( elem->GetID() );
7541               }
7542             }
7543             else {
7544               rmElemIds.push_back( elem->GetID() );
7545             }
7546           }
7547         }
7548         else {
7549         }
7550
7551         continue;
7552       } // poly element
7553
7554       // Regular elements
7555       // TODO not all the possible cases are solved. Find something more generic?
7556       switch ( entity ) {
7557       case SMDSEntity_Edge: //////// EDGE
7558       case SMDSEntity_Triangle: //// TRIANGLE
7559       case SMDSEntity_Quad_Triangle:
7560       case SMDSEntity_Tetra:
7561       case SMDSEntity_Quad_Tetra: // TETRAHEDRON
7562       {
7563         isOk = false;
7564         break;
7565       }
7566       case SMDSEntity_Quad_Edge:
7567       {
7568         isOk = false; // to linear EDGE ???????
7569         break;
7570       }
7571       case SMDSEntity_Quadrangle: //////////////////////////////////// QUADRANGLE
7572       {
7573         if ( nbUniqueNodes < 3 )
7574           isOk = false;
7575         else if ( nbRepl == 1 && curNodes[ iRepl[0]] == curNodes[( iRepl[0]+2 )%4 ])
7576           isOk = false; // opposite nodes stick
7577         break;
7578       }
7579       case SMDSEntity_Quad_Quadrangle: // Quadratic QUADRANGLE
7580       {
7581         //   1    5    2
7582         //    +---+---+
7583         //    |       |
7584         //   4+       +6
7585         //    |       |
7586         //    +---+---+
7587         //   0    7    3
7588         if (( nbUniqueNodes == 6 && nbRepl == 2 ) &&
7589             (( iRepl[0] == 1 && iRepl[1] == 4 && curNodes[1] == curNodes[0] ) ||
7590              ( iRepl[0] == 2 && iRepl[1] == 5 && curNodes[2] == curNodes[1] ) ||
7591              ( iRepl[0] == 3 && iRepl[1] == 6 && curNodes[3] == curNodes[2] ) ||
7592              ( iRepl[0] == 3 && iRepl[1] == 7 && curNodes[3] == curNodes[0] )))
7593         {
7594           isOk = true;
7595         }
7596         break;
7597       }
7598       case SMDSEntity_BiQuad_Quadrangle: // Bi-Quadratic QUADRANGLE
7599       {
7600         //   1    5    2
7601         //    +---+---+
7602         //    |       |
7603         //   4+  8+   +6
7604         //    |       |
7605         //    +---+---+
7606         //   0    7    3
7607         if (( nbUniqueNodes == 7 && nbRepl == 2 && iRepl[1] != 8 ) &&
7608             (( iRepl[0] == 1 && iRepl[1] == 4 && curNodes[1] == curNodes[0] ) ||
7609              ( iRepl[0] == 2 && iRepl[1] == 5 && curNodes[2] == curNodes[1] ) ||
7610              ( iRepl[0] == 3 && iRepl[1] == 6 && curNodes[3] == curNodes[2] ) ||
7611              ( iRepl[0] == 3 && iRepl[1] == 7 && curNodes[3] == curNodes[0] )))
7612         {
7613           isOk = true;
7614         }
7615         break;
7616       }
7617       case SMDSEntity_Penta: ///////////////////////////////////// PENTAHEDRON
7618       {
7619         isOk = false;
7620         if ( nbUniqueNodes == 4 ) {
7621           // ---------------------------------> tetrahedron
7622           if ( curNodes[3] == curNodes[4] &&
7623                curNodes[3] == curNodes[5] ) {
7624             // top nodes stick
7625             isOk = true;
7626           }
7627           else if ( curNodes[0] == curNodes[1] &&
7628                     curNodes[0] == curNodes[2] ) {
7629             // bottom nodes stick: set a top before
7630             uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7631             uniqueNodes[ 0 ] = curNodes [ 5 ];
7632             uniqueNodes[ 1 ] = curNodes [ 4 ];
7633             uniqueNodes[ 2 ] = curNodes [ 3 ];
7634             isOk = true;
7635           }
7636           else if (( curNodes[0] == curNodes[3] ) +
7637                    ( curNodes[1] == curNodes[4] ) +
7638                    ( curNodes[2] == curNodes[5] ) == 2 ) {
7639             // a lateral face turns into a line
7640             isOk = true;
7641           }
7642         }
7643         else if ( nbUniqueNodes == 5 ) {
7644           // PENTAHEDRON --------------------> pyramid
7645           if ( curNodes[0] == curNodes[3] )
7646           {
7647             uniqueNodes[ 0 ] = curNodes[ 1 ];
7648             uniqueNodes[ 1 ] = curNodes[ 4 ];
7649             uniqueNodes[ 2 ] = curNodes[ 5 ];
7650             uniqueNodes[ 3 ] = curNodes[ 2 ];
7651             uniqueNodes[ 4 ] = curNodes[ 0 ];
7652             isOk = true;
7653           }
7654           if ( curNodes[1] == curNodes[4] )
7655           {
7656             uniqueNodes[ 0 ] = curNodes[ 0 ];
7657             uniqueNodes[ 1 ] = curNodes[ 2 ];
7658             uniqueNodes[ 2 ] = curNodes[ 5 ];
7659             uniqueNodes[ 3 ] = curNodes[ 3 ];
7660             uniqueNodes[ 4 ] = curNodes[ 1 ];
7661             isOk = true;
7662           }
7663           if ( curNodes[2] == curNodes[5] )
7664           {
7665             uniqueNodes[ 0 ] = curNodes[ 0 ];
7666             uniqueNodes[ 1 ] = curNodes[ 3 ];
7667             uniqueNodes[ 2 ] = curNodes[ 4 ];
7668             uniqueNodes[ 3 ] = curNodes[ 1 ];
7669             uniqueNodes[ 4 ] = curNodes[ 2 ];
7670             isOk = true;
7671           }
7672         }
7673         break;
7674       }
7675       case SMDSEntity_Hexa:
7676       {
7677         //////////////////////////////////// HEXAHEDRON
7678         isOk = false;
7679         SMDS_VolumeTool hexa (elem);
7680         hexa.SetExternalNormal();
7681         if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7682           //////////////////////// HEX ---> tetrahedron
7683           for ( int iFace = 0; iFace < 6; iFace++ ) {
7684             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7685             if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7686                 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7687                 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7688               // one face turns into a point ...
7689               int  pickInd = ind[ 0 ];
7690               int iOppFace = hexa.GetOppFaceIndex( iFace );
7691               ind = hexa.GetFaceNodesIndices( iOppFace );
7692               int nbStick = 0;
7693               uniqueNodes.clear();
7694               for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7695                 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7696                   nbStick++;
7697                 else
7698                   uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7699               }
7700               if ( nbStick == 1 ) {
7701                 // ... and the opposite one - into a triangle.
7702                 // set a top node
7703                 uniqueNodes.push_back( curNodes[ pickInd ]);
7704                 isOk = true;
7705               }
7706               break;
7707             }
7708           }
7709         }
7710         else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7711           //////////////////////// HEX ---> prism
7712           int nbTria = 0, iTria[3];
7713           const int *ind; // indices of face nodes
7714           // look for triangular faces
7715           for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7716             ind = hexa.GetFaceNodesIndices( iFace );
7717             TIDSortedNodeSet faceNodes;
7718             for ( iCur = 0; iCur < 4; iCur++ )
7719               faceNodes.insert( curNodes[ind[iCur]] );
7720             if ( faceNodes.size() == 3 )
7721               iTria[ nbTria++ ] = iFace;
7722           }
7723           // check if triangles are opposite
7724           if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7725           {
7726             // set nodes of the bottom triangle
7727             ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7728             vector<int> indB;
7729             for ( iCur = 0; iCur < 4; iCur++ )
7730               if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7731                 indB.push_back( ind[iCur] );
7732             if ( !hexa.IsForward() )
7733               std::swap( indB[0], indB[2] );
7734             for ( iCur = 0; iCur < 3; iCur++ )
7735               uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7736             // set nodes of the top triangle
7737             const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7738             for ( iCur = 0; iCur < 3; ++iCur )
7739               for ( int j = 0; j < 4; ++j )
7740                 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7741                 {
7742                   uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7743                   break;
7744                 }
7745             isOk = true;
7746             break;
7747           }
7748         }
7749         else if (nbUniqueNodes == 5 && nbRepl == 3 ) {
7750           //////////////////// HEXAHEDRON ---> pyramid
7751           for ( int iFace = 0; iFace < 6; iFace++ ) {
7752             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7753             if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7754                 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7755                 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7756               // one face turns into a point ...
7757               int iOppFace = hexa.GetOppFaceIndex( iFace );
7758               ind = hexa.GetFaceNodesIndices( iOppFace );
7759               uniqueNodes.clear();
7760               for ( iCur = 0; iCur < 4; iCur++ ) {
7761                 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7762                   break;
7763                 else
7764                   uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7765               }
7766               if ( uniqueNodes.size() == 4 ) {
7767                 // ... and the opposite one is a quadrangle
7768                 // set a top node
7769                 const int* indTop = hexa.GetFaceNodesIndices( iFace );
7770                 uniqueNodes.push_back( curNodes[indTop[ 0 ]]);
7771                 isOk = true;
7772               }
7773               break;
7774             }
7775           }
7776         }
7777
7778         if ( !isOk && nbUniqueNodes > 4 ) {
7779           ////////////////// HEXAHEDRON ---> polyhedron
7780           hexa.SetExternalNormal();
7781           vector<const SMDS_MeshNode *> poly_nodes; poly_nodes.reserve( 6 * 4 );
7782           vector<int>                   quantities; quantities.reserve( 6 );
7783           for ( int iFace = 0; iFace < 6; iFace++ )
7784           {
7785             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7786             if ( curNodes[ind[0]] == curNodes[ind[2]] ||
7787                  curNodes[ind[1]] == curNodes[ind[3]] )
7788             {
7789               quantities.clear();
7790               break; // opposite nodes stick
7791             }
7792             nodeSet.clear();
7793             for ( iCur = 0; iCur < 4; iCur++ )
7794             {
7795               if ( nodeSet.insert( curNodes[ind[ iCur ]] ).second )
7796                 poly_nodes.push_back( curNodes[ind[ iCur ]]);
7797             }
7798             if ( nodeSet.size() < 3 )
7799               poly_nodes.resize( poly_nodes.size() - nodeSet.size() );
7800             else
7801               quantities.push_back( nodeSet.size() );
7802           }
7803           if ( quantities.size() >= 4 )
7804           {
7805             const SMDS_MeshElement* newElem = aMesh->AddPolyhedralVolume( poly_nodes, quantities );
7806             myLastCreatedElems.Append( newElem );
7807             if ( aShapeId && newElem )
7808               aMesh->SetMeshElementOnShape( newElem, aShapeId );
7809             rmElemIds.push_back( elem->GetID() );
7810           }
7811         }
7812         break;
7813       } // case HEXAHEDRON
7814
7815       default:
7816         isOk = false;
7817       } // switch ( nbNodes )
7818
7819     } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7820
7821     if ( isOk ) // a non-poly elem remains valid after sticking nodes
7822     {
7823       if ( nbNodes != nbUniqueNodes ||
7824            !aMesh->ChangeElementNodes( elem, & curNodes[0], nbNodes ))
7825       {
7826         elemType.Init( elem ).SetID( elem->GetID() );
7827
7828         SMESHDS_SubMesh * sm = aShapeId > 0 ? aMesh->MeshElements(aShapeId) : 0;
7829         aMesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
7830
7831         uniqueNodes.resize(nbUniqueNodes);
7832         SMDS_MeshElement* newElem = this->AddElement( uniqueNodes, elemType );
7833         if ( sm && newElem )
7834           sm->AddElement( newElem );
7835         if ( elem != newElem )
7836           ReplaceElemInGroups( elem, newElem, aMesh );
7837       }
7838     }
7839     else {
7840       // Remove invalid regular element or invalid polygon
7841       rmElemIds.push_back( elem->GetID() );
7842     }
7843
7844   } // loop on elements
7845
7846   // Remove bad elements, then equal nodes (order important)
7847
7848   Remove( rmElemIds, false );
7849   Remove( rmNodeIds, true );
7850
7851   return;
7852 }
7853
7854
7855 // ========================================================
7856 // class   : SortableElement
7857 // purpose : allow sorting elements basing on their nodes
7858 // ========================================================
7859 class SortableElement : public set <const SMDS_MeshElement*>
7860 {
7861 public:
7862
7863   SortableElement( const SMDS_MeshElement* theElem )
7864   {
7865     myElem = theElem;
7866     SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
7867     while ( nodeIt->more() )
7868       this->insert( nodeIt->next() );
7869   }
7870
7871   const SMDS_MeshElement* Get() const
7872   { return myElem; }
7873
7874 private:
7875   mutable const SMDS_MeshElement* myElem;
7876 };
7877
7878 //=======================================================================
7879 //function : FindEqualElements
7880 //purpose  : Return list of group of elements built on the same nodes.
7881 //           Search among theElements or in the whole mesh if theElements is empty
7882 //=======================================================================
7883
7884 void SMESH_MeshEditor::FindEqualElements(TIDSortedElemSet &        theElements,
7885                                          TListOfListOfElementsID & theGroupsOfElementsID)
7886 {
7887   myLastCreatedElems.Clear();
7888   myLastCreatedNodes.Clear();
7889
7890   typedef map< SortableElement, int > TMapOfNodeSet;
7891   typedef list<int> TGroupOfElems;
7892
7893   if ( theElements.empty() )
7894   { // get all elements in the mesh
7895     SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator();
7896     while ( eIt->more() )
7897       theElements.insert( theElements.end(), eIt->next() );
7898   }
7899
7900   vector< TGroupOfElems > arrayOfGroups;
7901   TGroupOfElems groupOfElems;
7902   TMapOfNodeSet mapOfNodeSet;
7903
7904   TIDSortedElemSet::iterator elemIt = theElements.begin();
7905   for ( int i = 0; elemIt != theElements.end(); ++elemIt )
7906   {
7907     const SMDS_MeshElement* curElem = *elemIt;
7908     SortableElement SE(curElem);
7909     // check uniqueness
7910     pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i));
7911     if ( !pp.second ) { // one more coincident elem
7912       TMapOfNodeSet::iterator& itSE = pp.first;
7913       int ind = (*itSE).second;
7914       arrayOfGroups[ind].push_back( curElem->GetID() );
7915     }
7916     else {
7917       arrayOfGroups.push_back( groupOfElems );
7918       arrayOfGroups.back().push_back( curElem->GetID() );
7919       i++;
7920     }
7921   }
7922
7923   groupOfElems.clear();
7924   vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
7925   for ( ; groupIt != arrayOfGroups.end(); ++groupIt )
7926   {
7927     if ( groupIt->size() > 1 ) {
7928       //groupOfElems.sort(); -- theElements is sorted already
7929       theGroupsOfElementsID.push_back( groupOfElems );
7930       theGroupsOfElementsID.back().splice( theGroupsOfElementsID.back().end(), *groupIt );
7931     }
7932   }
7933 }
7934
7935 //=======================================================================
7936 //function : MergeElements
7937 //purpose  : In each given group, substitute all elements by the first one.
7938 //=======================================================================
7939
7940 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
7941 {
7942   myLastCreatedElems.Clear();
7943   myLastCreatedNodes.Clear();
7944
7945   typedef list<int> TListOfIDs;
7946   TListOfIDs rmElemIds; // IDs of elems to remove
7947
7948   SMESHDS_Mesh* aMesh = GetMeshDS();
7949
7950   TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
7951   while ( groupsIt != theGroupsOfElementsID.end() ) {
7952     TListOfIDs& aGroupOfElemID = *groupsIt;
7953     aGroupOfElemID.sort();
7954     int elemIDToKeep = aGroupOfElemID.front();
7955     const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
7956     aGroupOfElemID.pop_front();
7957     TListOfIDs::iterator idIt = aGroupOfElemID.begin();
7958     while ( idIt != aGroupOfElemID.end() ) {
7959       int elemIDToRemove = *idIt;
7960       const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
7961       // add the kept element in groups of removed one (PAL15188)
7962       AddToSameGroups( elemToKeep, elemToRemove, aMesh );
7963       rmElemIds.push_back( elemIDToRemove );
7964       ++idIt;
7965     }
7966     ++groupsIt;
7967   }
7968
7969   Remove( rmElemIds, false );
7970 }
7971
7972 //=======================================================================
7973 //function : MergeEqualElements
7974 //purpose  : Remove all but one of elements built on the same nodes.
7975 //=======================================================================
7976
7977 void SMESH_MeshEditor::MergeEqualElements()
7978 {
7979   TIDSortedElemSet aMeshElements; /* empty input ==
7980                                      to merge equal elements in the whole mesh */
7981   TListOfListOfElementsID aGroupsOfElementsID;
7982   FindEqualElements(aMeshElements, aGroupsOfElementsID);
7983   MergeElements(aGroupsOfElementsID);
7984 }
7985
7986 //=======================================================================
7987 //function : findAdjacentFace
7988 //purpose  :
7989 //=======================================================================
7990
7991 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
7992                                                 const SMDS_MeshNode* n2,
7993                                                 const SMDS_MeshElement* elem)
7994 {
7995   TIDSortedElemSet elemSet, avoidSet;
7996   if ( elem )
7997     avoidSet.insert ( elem );
7998   return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
7999 }
8000
8001 //=======================================================================
8002 //function : findSegment
8003 //purpose  : Return a mesh segment by two nodes one of which can be medium
8004 //=======================================================================
8005
8006 static const SMDS_MeshElement* findSegment(const SMDS_MeshNode* n1,
8007                                            const SMDS_MeshNode* n2)
8008 {
8009   SMDS_ElemIteratorPtr it = n1->GetInverseElementIterator( SMDSAbs_Edge );
8010   while ( it->more() )
8011   {
8012     const SMDS_MeshElement* seg = it->next();
8013     if ( seg->GetNodeIndex( n2 ) >= 0 )
8014       return seg;
8015   }
8016   return 0;
8017 }
8018
8019 //=======================================================================
8020 //function : FindFreeBorder
8021 //purpose  :
8022 //=======================================================================
8023
8024 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
8025
8026 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode*             theFirstNode,
8027                                        const SMDS_MeshNode*             theSecondNode,
8028                                        const SMDS_MeshNode*             theLastNode,
8029                                        list< const SMDS_MeshNode* > &   theNodes,
8030                                        list< const SMDS_MeshElement* >& theFaces)
8031 {
8032   if ( !theFirstNode || !theSecondNode )
8033     return false;
8034   // find border face between theFirstNode and theSecondNode
8035   const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
8036   if ( !curElem )
8037     return false;
8038
8039   theFaces.push_back( curElem );
8040   theNodes.push_back( theFirstNode );
8041   theNodes.push_back( theSecondNode );
8042
8043   const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
8044   TIDSortedElemSet foundElems;
8045   bool needTheLast = ( theLastNode != 0 );
8046
8047   while ( nStart != theLastNode ) {
8048     if ( nStart == theFirstNode )
8049       return !needTheLast;
8050
8051     // find all free border faces sharing form nStart
8052
8053     list< const SMDS_MeshElement* > curElemList;
8054     list< const SMDS_MeshNode* >    nStartList;
8055     SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
8056     while ( invElemIt->more() ) {
8057       const SMDS_MeshElement* e = invElemIt->next();
8058       if ( e == curElem || foundElems.insert( e ).second ) {
8059         // get nodes
8060         int iNode = 0, nbNodes = e->NbNodes();
8061         vector<const SMDS_MeshNode*> nodes(nbNodes+1);
8062
8063         if ( e->IsQuadratic() ) {
8064           const SMDS_VtkFace* F =
8065             dynamic_cast<const SMDS_VtkFace*>(e);
8066           if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8067           // use special nodes iterator
8068           SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8069           while( anIter->more() ) {
8070             nodes[ iNode++ ] = cast2Node(anIter->next());
8071           }
8072         }
8073         else {
8074           SMDS_ElemIteratorPtr nIt = e->nodesIterator();
8075           while ( nIt->more() )
8076             nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
8077         }
8078         nodes[ iNode ] = nodes[ 0 ];
8079         // check 2 links
8080         for ( iNode = 0; iNode < nbNodes; iNode++ )
8081           if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
8082                (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
8083               ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
8084           {
8085             nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
8086             curElemList.push_back( e );
8087           }
8088       }
8089     }
8090     // analyse the found
8091
8092     int nbNewBorders = curElemList.size();
8093     if ( nbNewBorders == 0 ) {
8094       // no free border furthermore
8095       return !needTheLast;
8096     }
8097     else if ( nbNewBorders == 1 ) {
8098       // one more element found
8099       nIgnore = nStart;
8100       nStart = nStartList.front();
8101       curElem = curElemList.front();
8102       theFaces.push_back( curElem );
8103       theNodes.push_back( nStart );
8104     }
8105     else {
8106       // several continuations found
8107       list< const SMDS_MeshElement* >::iterator curElemIt;
8108       list< const SMDS_MeshNode* >::iterator nStartIt;
8109       // check if one of them reached the last node
8110       if ( needTheLast ) {
8111         for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8112              curElemIt!= curElemList.end();
8113              curElemIt++, nStartIt++ )
8114           if ( *nStartIt == theLastNode ) {
8115             theFaces.push_back( *curElemIt );
8116             theNodes.push_back( *nStartIt );
8117             return true;
8118           }
8119       }
8120       // find the best free border by the continuations
8121       list<const SMDS_MeshNode*>    contNodes[ 2 ], *cNL;
8122       list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
8123       for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8124            curElemIt!= curElemList.end();
8125            curElemIt++, nStartIt++ )
8126       {
8127         cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
8128         cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
8129         // find one more free border
8130         if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
8131           cNL->clear();
8132           cFL->clear();
8133         }
8134         else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
8135           // choice: clear a worse one
8136           int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
8137           int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
8138           contNodes[ iWorse ].clear();
8139           contFaces[ iWorse ].clear();
8140         }
8141       }
8142       if ( contNodes[0].empty() && contNodes[1].empty() )
8143         return false;
8144
8145       // append the best free border
8146       cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
8147       cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8148       theNodes.pop_back(); // remove nIgnore
8149       theNodes.pop_back(); // remove nStart
8150       theFaces.pop_back(); // remove curElem
8151       list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
8152       list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
8153       for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
8154       for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
8155       return true;
8156
8157     } // several continuations found
8158   } // while ( nStart != theLastNode )
8159
8160   return true;
8161 }
8162
8163 //=======================================================================
8164 //function : CheckFreeBorderNodes
8165 //purpose  : Return true if the tree nodes are on a free border
8166 //=======================================================================
8167
8168 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8169                                             const SMDS_MeshNode* theNode2,
8170                                             const SMDS_MeshNode* theNode3)
8171 {
8172   list< const SMDS_MeshNode* > nodes;
8173   list< const SMDS_MeshElement* > faces;
8174   return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8175 }
8176
8177 //=======================================================================
8178 //function : SewFreeBorder
8179 //purpose  :
8180 //warning  : for border-to-side sewing theSideSecondNode is considered as
8181 //           the last side node and theSideThirdNode is not used
8182 //=======================================================================
8183
8184 SMESH_MeshEditor::Sew_Error
8185 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8186                                  const SMDS_MeshNode* theBordSecondNode,
8187                                  const SMDS_MeshNode* theBordLastNode,
8188                                  const SMDS_MeshNode* theSideFirstNode,
8189                                  const SMDS_MeshNode* theSideSecondNode,
8190                                  const SMDS_MeshNode* theSideThirdNode,
8191                                  const bool           theSideIsFreeBorder,
8192                                  const bool           toCreatePolygons,
8193                                  const bool           toCreatePolyedrs)
8194 {
8195   myLastCreatedElems.Clear();
8196   myLastCreatedNodes.Clear();
8197
8198   Sew_Error aResult = SEW_OK;
8199
8200   // ====================================
8201   //    find side nodes and elements
8202   // ====================================
8203
8204   list< const SMDS_MeshNode* >    nSide[ 2 ];
8205   list< const SMDS_MeshElement* > eSide[ 2 ];
8206   list< const SMDS_MeshNode* >::iterator    nIt[ 2 ];
8207   list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8208
8209   // Free border 1
8210   // --------------
8211   if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8212                       nSide[0], eSide[0])) {
8213     MESSAGE(" Free Border 1 not found " );
8214     aResult = SEW_BORDER1_NOT_FOUND;
8215   }
8216   if (theSideIsFreeBorder) {
8217     // Free border 2
8218     // --------------
8219     if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8220                         nSide[1], eSide[1])) {
8221       MESSAGE(" Free Border 2 not found " );
8222       aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8223     }
8224   }
8225   if ( aResult != SEW_OK )
8226     return aResult;
8227
8228   if (!theSideIsFreeBorder) {
8229     // Side 2
8230     // --------------
8231
8232     // -------------------------------------------------------------------------
8233     // Algo:
8234     // 1. If nodes to merge are not coincident, move nodes of the free border
8235     //    from the coord sys defined by the direction from the first to last
8236     //    nodes of the border to the correspondent sys of the side 2
8237     // 2. On the side 2, find the links most co-directed with the correspondent
8238     //    links of the free border
8239     // -------------------------------------------------------------------------
8240
8241     // 1. Since sewing may break if there are volumes to split on the side 2,
8242     //    we wont move nodes but just compute new coordinates for them
8243     typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8244     TNodeXYZMap nBordXYZ;
8245     list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8246     list< const SMDS_MeshNode* >::iterator nBordIt;
8247
8248     gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8249     gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8250     gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8251     gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8252     double tol2 = 1.e-8;
8253     gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8254     if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8255       // Need node movement.
8256
8257       // find X and Z axes to create trsf
8258       gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8259       gp_Vec X = Zs ^ Zb;
8260       if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8261         // Zb || Zs
8262         X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8263
8264       // coord systems
8265       gp_Ax3 toBordAx( Pb1, Zb, X );
8266       gp_Ax3 fromSideAx( Ps1, Zs, X );
8267       gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8268       // set trsf
8269       gp_Trsf toBordSys, fromSide2Sys;
8270       toBordSys.SetTransformation( toBordAx );
8271       fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8272       fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8273
8274       // move
8275       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8276         const SMDS_MeshNode* n = *nBordIt;
8277         gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8278         toBordSys.Transforms( xyz );
8279         fromSide2Sys.Transforms( xyz );
8280         nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8281       }
8282     }
8283     else {
8284       // just insert nodes XYZ in the nBordXYZ map
8285       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8286         const SMDS_MeshNode* n = *nBordIt;
8287         nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8288       }
8289     }
8290
8291     // 2. On the side 2, find the links most co-directed with the correspondent
8292     //    links of the free border
8293
8294     list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8295     list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8296     sideNodes.push_back( theSideFirstNode );
8297
8298     bool hasVolumes = false;
8299     LinkID_Gen aLinkID_Gen( GetMeshDS() );
8300     set<long> foundSideLinkIDs, checkedLinkIDs;
8301     SMDS_VolumeTool volume;
8302     //const SMDS_MeshNode* faceNodes[ 4 ];
8303
8304     const SMDS_MeshNode*    sideNode;
8305     const SMDS_MeshElement* sideElem  = 0;
8306     const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8307     const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8308     nBordIt = bordNodes.begin();
8309     nBordIt++;
8310     // border node position and border link direction to compare with
8311     gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8312     gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8313     // choose next side node by link direction or by closeness to
8314     // the current border node:
8315     bool searchByDir = ( *nBordIt != theBordLastNode );
8316     do {
8317       // find the next node on the Side 2
8318       sideNode = 0;
8319       double maxDot = -DBL_MAX, minDist = DBL_MAX;
8320       long linkID;
8321       checkedLinkIDs.clear();
8322       gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8323
8324       // loop on inverse elements of current node (prevSideNode) on the Side 2
8325       SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8326       while ( invElemIt->more() )
8327       {
8328         const SMDS_MeshElement* elem = invElemIt->next();
8329         // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8330         int iPrevNode = 0, iNode = 0, nbNodes = elem->NbNodes();
8331         vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8332         bool isVolume = volume.Set( elem );
8333         const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8334         if ( isVolume ) // --volume
8335           hasVolumes = true;
8336         else if ( elem->GetType()==SMDSAbs_Face ) { // --face
8337           // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8338           if(elem->IsQuadratic()) {
8339             const SMDS_VtkFace* F =
8340               dynamic_cast<const SMDS_VtkFace*>(elem);
8341             if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8342             // use special nodes iterator
8343             SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8344             while( anIter->more() ) {
8345               nodes[ iNode ] = cast2Node(anIter->next());
8346               if ( nodes[ iNode++ ] == prevSideNode )
8347                 iPrevNode = iNode - 1;
8348             }
8349           }
8350           else {
8351             SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
8352             while ( nIt->more() ) {
8353               nodes[ iNode ] = cast2Node( nIt->next() );
8354               if ( nodes[ iNode++ ] == prevSideNode )
8355                 iPrevNode = iNode - 1;
8356             }
8357           }
8358           // there are 2 links to check
8359           nbNodes = 2;
8360         }
8361         else // --edge
8362           continue;
8363         // loop on links, to be precise, on the second node of links
8364         for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8365           const SMDS_MeshNode* n = nodes[ iNode ];
8366           if ( isVolume ) {
8367             if ( !volume.IsLinked( n, prevSideNode ))
8368               continue;
8369           }
8370           else {
8371             if ( iNode ) // a node before prevSideNode
8372               n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8373             else         // a node after prevSideNode
8374               n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8375           }
8376           // check if this link was already used
8377           long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8378           bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8379           if (!isJustChecked &&
8380               foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8381           {
8382             // test a link geometrically
8383             gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8384             bool linkIsBetter = false;
8385             double dot = 0.0, dist = 0.0;
8386             if ( searchByDir ) { // choose most co-directed link
8387               dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8388               linkIsBetter = ( dot > maxDot );
8389             }
8390             else { // choose link with the node closest to bordPos
8391               dist = ( nextXYZ - bordPos ).SquareModulus();
8392               linkIsBetter = ( dist < minDist );
8393             }
8394             if ( linkIsBetter ) {
8395               maxDot = dot;
8396               minDist = dist;
8397               linkID = iLink;
8398               sideNode = n;
8399               sideElem = elem;
8400             }
8401           }
8402         }
8403       } // loop on inverse elements of prevSideNode
8404
8405       if ( !sideNode ) {
8406         MESSAGE(" Cant find path by links of the Side 2 ");
8407         return SEW_BAD_SIDE_NODES;
8408       }
8409       sideNodes.push_back( sideNode );
8410       sideElems.push_back( sideElem );
8411       foundSideLinkIDs.insert ( linkID );
8412       prevSideNode = sideNode;
8413
8414       if ( *nBordIt == theBordLastNode )
8415         searchByDir = false;
8416       else {
8417         // find the next border link to compare with
8418         gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8419         searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8420         // move to next border node if sideNode is before forward border node (bordPos)
8421         while ( *nBordIt != theBordLastNode && !searchByDir ) {
8422           prevBordNode = *nBordIt;
8423           nBordIt++;
8424           bordPos = nBordXYZ[ *nBordIt ];
8425           bordDir = bordPos - nBordXYZ[ prevBordNode ];
8426           searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8427         }
8428       }
8429     }
8430     while ( sideNode != theSideSecondNode );
8431
8432     if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8433       MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8434       return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8435     }
8436   } // end nodes search on the side 2
8437
8438   // ============================
8439   // sew the border to the side 2
8440   // ============================
8441
8442   int nbNodes[]  = { (int)nSide[0].size(), (int)nSide[1].size() };
8443   int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8444
8445   bool toMergeConformal = ( nbNodes[0] == nbNodes[1] );
8446   if ( toMergeConformal && toCreatePolygons )
8447   {
8448     // do not merge quadrangles if polygons are OK (IPAL0052824)
8449     eIt[0] = eSide[0].begin();
8450     eIt[1] = eSide[1].begin();
8451     bool allQuads[2] = { true, true };
8452     for ( int iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8453       for ( ; allQuads[iBord] && eIt[iBord] != eSide[iBord].end(); ++eIt[iBord] )
8454         allQuads[iBord] = ( (*eIt[iBord])->NbCornerNodes() == 4 );
8455     }
8456     toMergeConformal = ( !allQuads[0] && !allQuads[1] );
8457   }
8458
8459   TListOfListOfNodes nodeGroupsToMerge;
8460   if (( toMergeConformal ) ||
8461       ( theSideIsFreeBorder && !theSideThirdNode )) {
8462
8463     // all nodes are to be merged
8464
8465     for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8466          nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8467          nIt[0]++, nIt[1]++ )
8468     {
8469       nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8470       nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8471       nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8472     }
8473   }
8474   else {
8475
8476     // insert new nodes into the border and the side to get equal nb of segments
8477
8478     // get normalized parameters of nodes on the borders
8479     vector< double > param[ 2 ];
8480     param[0].resize( maxNbNodes );
8481     param[1].resize( maxNbNodes );
8482     int iNode, iBord;
8483     for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8484       list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8485       list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8486       const SMDS_MeshNode* nPrev = *nIt;
8487       double bordLength = 0;
8488       for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8489         const SMDS_MeshNode* nCur = *nIt;
8490         gp_XYZ segment (nCur->X() - nPrev->X(),
8491                         nCur->Y() - nPrev->Y(),
8492                         nCur->Z() - nPrev->Z());
8493         double segmentLen = segment.Modulus();
8494         bordLength += segmentLen;
8495         param[ iBord ][ iNode ] = bordLength;
8496         nPrev = nCur;
8497       }
8498       // normalize within [0,1]
8499       for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8500         param[ iBord ][ iNode ] /= bordLength;
8501       }
8502     }
8503
8504     // loop on border segments
8505     const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8506     int i[ 2 ] = { 0, 0 };
8507     nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8508     nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8509
8510     TElemOfNodeListMap insertMap;
8511     TElemOfNodeListMap::iterator insertMapIt;
8512     // insertMap is
8513     // key:   elem to insert nodes into
8514     // value: 2 nodes to insert between + nodes to be inserted
8515     do {
8516       bool next[ 2 ] = { false, false };
8517
8518       // find min adjacent segment length after sewing
8519       double nextParam = 10., prevParam = 0;
8520       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8521         if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8522           nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8523         if ( i[ iBord ] > 0 )
8524           prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8525       }
8526       double  minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8527       double  maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8528       double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8529
8530       // choose to insert or to merge nodes
8531       double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8532       if ( Abs( du ) <= minSegLen * 0.2 ) {
8533         // merge
8534         // ------
8535         nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8536         const SMDS_MeshNode* n0 = *nIt[0];
8537         const SMDS_MeshNode* n1 = *nIt[1];
8538         nodeGroupsToMerge.back().push_back( n1 );
8539         nodeGroupsToMerge.back().push_back( n0 );
8540         // position of node of the border changes due to merge
8541         param[ 0 ][ i[0] ] += du;
8542         // move n1 for the sake of elem shape evaluation during insertion.
8543         // n1 will be removed by MergeNodes() anyway
8544         const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8545         next[0] = next[1] = true;
8546       }
8547       else {
8548         // insert
8549         // ------
8550         int intoBord = ( du < 0 ) ? 0 : 1;
8551         const SMDS_MeshElement* elem = *eIt [ intoBord ];
8552         const SMDS_MeshNode*    n1   = nPrev[ intoBord ];
8553         const SMDS_MeshNode*    n2   = *nIt [ intoBord ];
8554         const SMDS_MeshNode*    nIns = *nIt [ 1 - intoBord ];
8555         if ( intoBord == 1 ) {
8556           // move node of the border to be on a link of elem of the side
8557           gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8558           gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8559           double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8560           gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8561           GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8562         }
8563         insertMapIt = insertMap.find( elem );
8564         bool  notFound = ( insertMapIt == insertMap.end() );
8565         bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8566         if ( otherLink ) {
8567           // insert into another link of the same element:
8568           // 1. perform insertion into the other link of the elem
8569           list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8570           const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8571           const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8572           InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8573           // 2. perform insertion into the link of adjacent faces
8574           while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem )) {
8575             InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8576           }
8577           while ( const SMDS_MeshElement* seg = findSegment( n12, n22 )) {
8578             InsertNodesIntoLink( seg, n12, n22, nodeList );
8579           }
8580           if (toCreatePolyedrs) {
8581             // perform insertion into the links of adjacent volumes
8582             UpdateVolumes(n12, n22, nodeList);
8583           }
8584           // 3. find an element appeared on n1 and n2 after the insertion
8585           insertMap.erase( elem );
8586           elem = findAdjacentFace( n1, n2, 0 );
8587         }
8588         if ( notFound || otherLink ) {
8589           // add element and nodes of the side into the insertMap
8590           insertMapIt = insertMap.insert( make_pair( elem, list<const SMDS_MeshNode*>() )).first;
8591           (*insertMapIt).second.push_back( n1 );
8592           (*insertMapIt).second.push_back( n2 );
8593         }
8594         // add node to be inserted into elem
8595         (*insertMapIt).second.push_back( nIns );
8596         next[ 1 - intoBord ] = true;
8597       }
8598
8599       // go to the next segment
8600       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8601         if ( next[ iBord ] ) {
8602           if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8603             eIt[ iBord ]++;
8604           nPrev[ iBord ] = *nIt[ iBord ];
8605           nIt[ iBord ]++; i[ iBord ]++;
8606         }
8607       }
8608     }
8609     while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8610
8611     // perform insertion of nodes into elements
8612
8613     for (insertMapIt = insertMap.begin();
8614          insertMapIt != insertMap.end();
8615          insertMapIt++ )
8616     {
8617       const SMDS_MeshElement* elem = (*insertMapIt).first;
8618       list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8619       const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8620       const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8621
8622       InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8623
8624       while ( const SMDS_MeshElement* seg = findSegment( n1, n2 )) {
8625         InsertNodesIntoLink( seg, n1, n2, nodeList );
8626       }
8627
8628       if ( !theSideIsFreeBorder ) {
8629         // look for and insert nodes into the faces adjacent to elem
8630         while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem )) {
8631           InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8632         }
8633       }
8634       if (toCreatePolyedrs) {
8635         // perform insertion into the links of adjacent volumes
8636         UpdateVolumes(n1, n2, nodeList);
8637       }
8638     }
8639   } // end: insert new nodes
8640
8641   MergeNodes ( nodeGroupsToMerge );
8642
8643
8644   // Remove coincident segments
8645
8646   // get new segments
8647   TIDSortedElemSet segments;
8648   SMESH_SequenceOfElemPtr newFaces;
8649   for ( int i = 1; i <= myLastCreatedElems.Length(); ++i )
8650   {
8651     if ( !myLastCreatedElems(i) ) continue;
8652     if ( myLastCreatedElems(i)->GetType() == SMDSAbs_Edge )
8653       segments.insert( segments.end(), myLastCreatedElems(i) );
8654     else
8655       newFaces.Append( myLastCreatedElems(i) );
8656   }
8657   // get segments adjacent to merged nodes
8658   TListOfListOfNodes::iterator groupIt = nodeGroupsToMerge.begin();
8659   for ( ; groupIt != nodeGroupsToMerge.end(); groupIt++ )
8660   {
8661     const list<const SMDS_MeshNode*>& nodes = *groupIt;
8662     SMDS_ElemIteratorPtr segIt = nodes.front()->GetInverseElementIterator( SMDSAbs_Edge );
8663     while ( segIt->more() )
8664       segments.insert( segIt->next() );
8665   }
8666
8667   // find coincident
8668   TListOfListOfElementsID equalGroups;
8669   if ( !segments.empty() )
8670     FindEqualElements( segments, equalGroups );
8671   if ( !equalGroups.empty() )
8672   {
8673     // remove from segments those that will be removed
8674     TListOfListOfElementsID::iterator itGroups = equalGroups.begin();
8675     for ( ; itGroups != equalGroups.end(); ++itGroups )
8676     {
8677       list< int >& group = *itGroups;
8678       list< int >::iterator id = group.begin();
8679       for ( ++id; id != group.end(); ++id )
8680         if ( const SMDS_MeshElement* seg = GetMeshDS()->FindElement( *id ))
8681           segments.erase( seg );
8682     }
8683     // remove equal segments
8684     MergeElements( equalGroups );
8685
8686     // restore myLastCreatedElems
8687     myLastCreatedElems = newFaces;
8688     TIDSortedElemSet::iterator seg = segments.begin();
8689     for ( ; seg != segments.end(); ++seg )
8690       myLastCreatedElems.Append( *seg );
8691   }
8692
8693   return aResult;
8694 }
8695
8696 //=======================================================================
8697 //function : InsertNodesIntoLink
8698 //purpose  : insert theNodesToInsert into theElement between theBetweenNode1
8699 //           and theBetweenNode2 and split theElement
8700 //=======================================================================
8701
8702 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement*     theElement,
8703                                            const SMDS_MeshNode*        theBetweenNode1,
8704                                            const SMDS_MeshNode*        theBetweenNode2,
8705                                            list<const SMDS_MeshNode*>& theNodesToInsert,
8706                                            const bool                  toCreatePoly)
8707 {
8708   if ( !theElement ) return;
8709
8710   SMESHDS_Mesh *aMesh = GetMeshDS();
8711   vector<const SMDS_MeshElement*> newElems;
8712
8713   if ( theElement->GetType() == SMDSAbs_Edge )
8714   {
8715     theNodesToInsert.push_front( theBetweenNode1 );
8716     theNodesToInsert.push_back ( theBetweenNode2 );
8717     list<const SMDS_MeshNode*>::iterator n = theNodesToInsert.begin();
8718     const SMDS_MeshNode* n1 = *n;
8719     for ( ++n; n != theNodesToInsert.end(); ++n )
8720     {
8721       const SMDS_MeshNode* n2 = *n;
8722       if ( const SMDS_MeshElement* seg = aMesh->FindEdge( n1, n2 ))
8723         AddToSameGroups( seg, theElement, aMesh );
8724       else
8725         newElems.push_back( aMesh->AddEdge ( n1, n2 ));
8726       n1 = n2;
8727     }
8728     theNodesToInsert.pop_front();
8729     theNodesToInsert.pop_back();
8730
8731     if ( theElement->IsQuadratic() ) // add a not split part
8732     {
8733       vector<const SMDS_MeshNode*> nodes( theElement->begin_nodes(),
8734                                           theElement->end_nodes() );
8735       int iOther = 0, nbN = nodes.size();
8736       for ( ; iOther < nbN; ++iOther )
8737         if ( nodes[iOther] != theBetweenNode1 &&
8738              nodes[iOther] != theBetweenNode2 )
8739           break;
8740       if      ( iOther == 0 )
8741       {
8742         if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[0], nodes[1] ))
8743           AddToSameGroups( seg, theElement, aMesh );
8744         else
8745           newElems.push_back( aMesh->AddEdge ( nodes[0], nodes[1] ));
8746       }
8747       else if ( iOther == 2 )
8748       {
8749         if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[1], nodes[2] ))
8750           AddToSameGroups( seg, theElement, aMesh );
8751         else
8752           newElems.push_back( aMesh->AddEdge ( nodes[1], nodes[2] ));
8753       }
8754     }
8755     // treat new elements
8756     for ( size_t i = 0; i < newElems.size(); ++i )
8757       if ( newElems[i] )
8758       {
8759         aMesh->SetMeshElementOnShape( newElems[i], theElement->getshapeId() );
8760         myLastCreatedElems.Append( newElems[i] );
8761       }
8762     ReplaceElemInGroups( theElement, newElems, aMesh );
8763     aMesh->RemoveElement( theElement );
8764     return;
8765
8766   } // if ( theElement->GetType() == SMDSAbs_Edge )
8767
8768   const SMDS_MeshElement* theFace = theElement;
8769   if ( theFace->GetType() != SMDSAbs_Face ) return;
8770
8771   // find indices of 2 link nodes and of the rest nodes
8772   int iNode = 0, il1, il2, i3, i4;
8773   il1 = il2 = i3 = i4 = -1;
8774   vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8775
8776   SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8777   while ( nodeIt->more() ) {
8778     const SMDS_MeshNode* n = nodeIt->next();
8779     if ( n == theBetweenNode1 )
8780       il1 = iNode;
8781     else if ( n == theBetweenNode2 )
8782       il2 = iNode;
8783     else if ( i3 < 0 )
8784       i3 = iNode;
8785     else
8786       i4 = iNode;
8787     nodes[ iNode++ ] = n;
8788   }
8789   if ( il1 < 0 || il2 < 0 || i3 < 0 )
8790     return ;
8791
8792   // arrange link nodes to go one after another regarding the face orientation
8793   bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8794   list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8795   if ( reverse ) {
8796     iNode = il1;
8797     il1 = il2;
8798     il2 = iNode;
8799     aNodesToInsert.reverse();
8800   }
8801   // check that not link nodes of a quadrangles are in good order
8802   int nbFaceNodes = theFace->NbNodes();
8803   if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8804     iNode = i3;
8805     i3 = i4;
8806     i4 = iNode;
8807   }
8808
8809   if (toCreatePoly || theFace->IsPoly()) {
8810
8811     iNode = 0;
8812     vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8813
8814     // add nodes of face up to first node of link
8815     bool isFLN = false;
8816
8817     if ( theFace->IsQuadratic() ) {
8818       const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>(theFace);
8819       if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8820       // use special nodes iterator
8821       SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8822       while( anIter->more()  && !isFLN ) {
8823         const SMDS_MeshNode* n = cast2Node(anIter->next());
8824         poly_nodes[iNode++] = n;
8825         if (n == nodes[il1]) {
8826           isFLN = true;
8827         }
8828       }
8829       // add nodes to insert
8830       list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8831       for (; nIt != aNodesToInsert.end(); nIt++) {
8832         poly_nodes[iNode++] = *nIt;
8833       }
8834       // add nodes of face starting from last node of link
8835       while ( anIter->more() ) {
8836         poly_nodes[iNode++] = cast2Node(anIter->next());
8837       }
8838     }
8839     else {
8840       SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8841       while ( nodeIt->more() && !isFLN ) {
8842         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8843         poly_nodes[iNode++] = n;
8844         if (n == nodes[il1]) {
8845           isFLN = true;
8846         }
8847       }
8848       // add nodes to insert
8849       list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8850       for (; nIt != aNodesToInsert.end(); nIt++) {
8851         poly_nodes[iNode++] = *nIt;
8852       }
8853       // add nodes of face starting from last node of link
8854       while ( nodeIt->more() ) {
8855         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8856         poly_nodes[iNode++] = n;
8857       }
8858     }
8859
8860     // make a new face
8861     newElems.push_back( aMesh->AddPolygonalFace( poly_nodes ));
8862   }
8863
8864   else if ( !theFace->IsQuadratic() )
8865   {
8866     // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8867     int nbLinkNodes = 2 + aNodesToInsert.size();
8868     //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8869     vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8870     linkNodes[ 0 ] = nodes[ il1 ];
8871     linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8872     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8873     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8874       linkNodes[ iNode++ ] = *nIt;
8875     }
8876     // decide how to split a quadrangle: compare possible variants
8877     // and choose which of splits to be a quadrangle
8878     int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad = 0;
8879     if ( nbFaceNodes == 3 ) {
8880       iBestQuad = nbSplits;
8881       i4 = i3;
8882     }
8883     else if ( nbFaceNodes == 4 ) {
8884       SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8885       double aBestRate = DBL_MAX;
8886       for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8887         i1 = 0; i2 = 1;
8888         double aBadRate = 0;
8889         // evaluate elements quality
8890         for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8891           if ( iSplit == iQuad ) {
8892             SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8893                                    linkNodes[ i2++ ],
8894                                    nodes[ i3 ],
8895                                    nodes[ i4 ]);
8896             aBadRate += getBadRate( &quad, aCrit );
8897           }
8898           else {
8899             SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8900                                    linkNodes[ i2++ ],
8901                                    nodes[ iSplit < iQuad ? i4 : i3 ]);
8902             aBadRate += getBadRate( &tria, aCrit );
8903           }
8904         }
8905         // choice
8906         if ( aBadRate < aBestRate ) {
8907           iBestQuad = iQuad;
8908           aBestRate = aBadRate;
8909         }
8910       }
8911     }
8912
8913     // create new elements
8914     i1 = 0; i2 = 1;
8915     for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ )
8916     {
8917       if ( iSplit == iBestQuad )
8918         newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8919                                             linkNodes[ i2++ ],
8920                                             nodes[ i3 ],
8921                                             nodes[ i4 ]));
8922       else
8923         newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8924                                             linkNodes[ i2++ ],
8925                                             nodes[ iSplit < iBestQuad ? i4 : i3 ]));
8926     }
8927
8928     const SMDS_MeshNode* newNodes[ 4 ];
8929     newNodes[ 0 ] = linkNodes[ i1 ];
8930     newNodes[ 1 ] = linkNodes[ i2 ];
8931     newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
8932     newNodes[ 3 ] = nodes[ i4 ];
8933     if (iSplit == iBestQuad)
8934       newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] ));
8935     else
8936       newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] ));
8937
8938   } // end if(!theFace->IsQuadratic())
8939
8940   else { // theFace is quadratic
8941     // we have to split theFace on simple triangles and one simple quadrangle
8942     int tmp = il1/2;
8943     int nbshift = tmp*2;
8944     // shift nodes in nodes[] by nbshift
8945     int i,j;
8946     for(i=0; i<nbshift; i++) {
8947       const SMDS_MeshNode* n = nodes[0];
8948       for(j=0; j<nbFaceNodes-1; j++) {
8949         nodes[j] = nodes[j+1];
8950       }
8951       nodes[nbFaceNodes-1] = n;
8952     }
8953     il1 = il1 - nbshift;
8954     // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
8955     //   n0      n1     n2    n0      n1     n2
8956     //     +-----+-----+        +-----+-----+
8957     //      \         /         |           |
8958     //       \       /          |           |
8959     //      n5+     +n3       n7+           +n3
8960     //         \   /            |           |
8961     //          \ /             |           |
8962     //           +              +-----+-----+
8963     //           n4           n6      n5     n4
8964
8965     // create new elements
8966     int n1,n2,n3;
8967     if ( nbFaceNodes == 6 ) { // quadratic triangle
8968       newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8969       if ( theFace->IsMediumNode(nodes[il1]) ) {
8970         // create quadrangle
8971         newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[5] ));
8972         n1 = 1;
8973         n2 = 2;
8974         n3 = 3;
8975       }
8976       else {
8977         // create quadrangle
8978         newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[5] ));
8979         n1 = 0;
8980         n2 = 1;
8981         n3 = 5;
8982       }
8983     }
8984     else { // nbFaceNodes==8 - quadratic quadrangle
8985       newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8986       newElems.push_back( aMesh->AddFace( nodes[5], nodes[6], nodes[7] ));
8987       newElems.push_back( aMesh->AddFace( nodes[5], nodes[7], nodes[3] ));
8988       if ( theFace->IsMediumNode( nodes[ il1 ])) {
8989         // create quadrangle
8990         newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[7] ));
8991         n1 = 1;
8992         n2 = 2;
8993         n3 = 3;
8994       }
8995       else {
8996         // create quadrangle
8997         newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[7] ));
8998         n1 = 0;
8999         n2 = 1;
9000         n3 = 7;
9001       }
9002     }
9003     // create needed triangles using n1,n2,n3 and inserted nodes
9004     int nbn = 2 + aNodesToInsert.size();
9005     vector<const SMDS_MeshNode*> aNodes(nbn);
9006     aNodes[0    ] = nodes[n1];
9007     aNodes[nbn-1] = nodes[n2];
9008     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9009     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
9010       aNodes[iNode++] = *nIt;
9011     }
9012     for ( i = 1; i < nbn; i++ )
9013       newElems.push_back( aMesh->AddFace( aNodes[i-1], aNodes[i], nodes[n3] ));
9014   }
9015
9016   // remove the old face
9017   for ( size_t i = 0; i < newElems.size(); ++i )
9018     if ( newElems[i] )
9019     {
9020       aMesh->SetMeshElementOnShape( newElems[i], theFace->getshapeId() );
9021       myLastCreatedElems.Append( newElems[i] );
9022     }
9023   ReplaceElemInGroups( theFace, newElems, aMesh );
9024   aMesh->RemoveElement(theFace);
9025
9026 } // InsertNodesIntoLink()
9027
9028 //=======================================================================
9029 //function : UpdateVolumes
9030 //purpose  :
9031 //=======================================================================
9032
9033 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode*        theBetweenNode1,
9034                                       const SMDS_MeshNode*        theBetweenNode2,
9035                                       list<const SMDS_MeshNode*>& theNodesToInsert)
9036 {
9037   myLastCreatedElems.Clear();
9038   myLastCreatedNodes.Clear();
9039
9040   SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
9041   while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
9042     const SMDS_MeshElement* elem = invElemIt->next();
9043
9044     // check, if current volume has link theBetweenNode1 - theBetweenNode2
9045     SMDS_VolumeTool aVolume (elem);
9046     if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
9047       continue;
9048
9049     // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
9050     int iface, nbFaces = aVolume.NbFaces();
9051     vector<const SMDS_MeshNode *> poly_nodes;
9052     vector<int> quantities (nbFaces);
9053
9054     for (iface = 0; iface < nbFaces; iface++) {
9055       int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
9056       // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
9057       const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
9058
9059       for (int inode = 0; inode < nbFaceNodes; inode++) {
9060         poly_nodes.push_back(faceNodes[inode]);
9061
9062         if (nbInserted == 0) {
9063           if (faceNodes[inode] == theBetweenNode1) {
9064             if (faceNodes[inode + 1] == theBetweenNode2) {
9065               nbInserted = theNodesToInsert.size();
9066
9067               // add nodes to insert
9068               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
9069               for (; nIt != theNodesToInsert.end(); nIt++) {
9070                 poly_nodes.push_back(*nIt);
9071               }
9072             }
9073           }
9074           else if (faceNodes[inode] == theBetweenNode2) {
9075             if (faceNodes[inode + 1] == theBetweenNode1) {
9076               nbInserted = theNodesToInsert.size();
9077
9078               // add nodes to insert in reversed order
9079               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
9080               nIt--;
9081               for (; nIt != theNodesToInsert.begin(); nIt--) {
9082                 poly_nodes.push_back(*nIt);
9083               }
9084               poly_nodes.push_back(*nIt);
9085             }
9086           }
9087           else {
9088           }
9089         }
9090       }
9091       quantities[iface] = nbFaceNodes + nbInserted;
9092     }
9093
9094     // Replace the volume
9095     SMESHDS_Mesh *aMesh = GetMeshDS();
9096
9097     if ( SMDS_MeshElement* newElem = aMesh->AddPolyhedralVolume( poly_nodes, quantities ))
9098     {
9099       aMesh->SetMeshElementOnShape( newElem, elem->getshapeId() );
9100       myLastCreatedElems.Append( newElem );
9101       ReplaceElemInGroups( elem, newElem, aMesh );
9102     }
9103     aMesh->RemoveElement( elem );
9104   }
9105 }
9106
9107 namespace
9108 {
9109   //================================================================================
9110   /*!
9111    * \brief Transform any volume into data of SMDSEntity_Polyhedra
9112    */
9113   //================================================================================
9114
9115   void volumeToPolyhedron( const SMDS_MeshElement*         elem,
9116                            vector<const SMDS_MeshNode *> & nodes,
9117                            vector<int> &                   nbNodeInFaces )
9118   {
9119     nodes.clear();
9120     nbNodeInFaces.clear();
9121     SMDS_VolumeTool vTool ( elem );
9122     for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
9123     {
9124       const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
9125       nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
9126       nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
9127     }
9128   }
9129 }
9130
9131 //=======================================================================
9132 /*!
9133  * \brief Convert elements contained in a sub-mesh to quadratic
9134  * \return int - nb of checked elements
9135  */
9136 //=======================================================================
9137
9138 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh *   theSm,
9139                                              SMESH_MesherHelper& theHelper,
9140                                              const bool          theForce3d)
9141 {
9142   int nbElem = 0;
9143   if( !theSm ) return nbElem;
9144
9145   vector<int> nbNodeInFaces;
9146   vector<const SMDS_MeshNode *> nodes;
9147   SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
9148   while(ElemItr->more())
9149   {
9150     nbElem++;
9151     const SMDS_MeshElement* elem = ElemItr->next();
9152     if( !elem ) continue;
9153
9154     // analyse a necessity of conversion
9155     const SMDSAbs_ElementType aType = elem->GetType();
9156     if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
9157       continue;
9158     const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
9159     bool hasCentralNodes = false;
9160     if ( elem->IsQuadratic() )
9161     {
9162       bool alreadyOK;
9163       switch ( aGeomType ) {
9164       case SMDSEntity_Quad_Triangle:
9165       case SMDSEntity_Quad_Quadrangle:
9166       case SMDSEntity_Quad_Hexa:
9167         alreadyOK = !theHelper.GetIsBiQuadratic(); break;
9168
9169       case SMDSEntity_BiQuad_Triangle:
9170       case SMDSEntity_BiQuad_Quadrangle:
9171       case SMDSEntity_TriQuad_Hexa:
9172         alreadyOK = theHelper.GetIsBiQuadratic();
9173         hasCentralNodes = true;
9174         break;
9175       default:
9176         alreadyOK = true;
9177       }
9178       // take into account already present modium nodes
9179       switch ( aType ) {
9180       case SMDSAbs_Volume:
9181         theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
9182       case SMDSAbs_Face:
9183         theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
9184       case SMDSAbs_Edge:
9185         theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
9186       default:;
9187       }
9188       if ( alreadyOK )
9189         continue;
9190     }
9191     // get elem data needed to re-create it
9192     //
9193     const int id      = elem->GetID();
9194     const int nbNodes = elem->NbCornerNodes();
9195     nodes.assign(elem->begin_nodes(), elem->end_nodes());
9196     if ( aGeomType == SMDSEntity_Polyhedra )
9197       nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
9198     else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
9199       volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9200
9201     // remove a linear element
9202     GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9203
9204     // remove central nodes of biquadratic elements (biquad->quad convertion)
9205     if ( hasCentralNodes )
9206       for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
9207         if ( nodes[i]->NbInverseElements() == 0 )
9208           GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
9209
9210     const SMDS_MeshElement* NewElem = 0;
9211
9212     switch( aType )
9213     {
9214     case SMDSAbs_Edge :
9215       {
9216         NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9217         break;
9218       }
9219     case SMDSAbs_Face :
9220       {
9221         switch(nbNodes)
9222         {
9223         case 3:
9224           NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9225           break;
9226         case 4:
9227           NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9228           break;
9229         default:
9230           NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9231         }
9232         break;
9233       }
9234     case SMDSAbs_Volume :
9235       {
9236         switch( aGeomType )
9237         {
9238         case SMDSEntity_Tetra:
9239           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9240           break;
9241         case SMDSEntity_Pyramid:
9242           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9243           break;
9244         case SMDSEntity_Penta:
9245           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9246           break;
9247         case SMDSEntity_Hexa:
9248         case SMDSEntity_Quad_Hexa:
9249         case SMDSEntity_TriQuad_Hexa:
9250           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9251                                         nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9252           break;
9253         case SMDSEntity_Hexagonal_Prism:
9254         default:
9255           NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9256         }
9257         break;
9258       }
9259     default :
9260       continue;
9261     }
9262     ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9263     if( NewElem && NewElem->getshapeId() < 1 )
9264       theSm->AddElement( NewElem );
9265   }
9266   return nbElem;
9267 }
9268 //=======================================================================
9269 //function : ConvertToQuadratic
9270 //purpose  :
9271 //=======================================================================
9272
9273 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
9274 {
9275   SMESHDS_Mesh* meshDS = GetMeshDS();
9276
9277   SMESH_MesherHelper aHelper(*myMesh);
9278
9279   aHelper.SetIsQuadratic( true );
9280   aHelper.SetIsBiQuadratic( theToBiQuad );
9281   aHelper.SetElementsOnShape(true);
9282   aHelper.ToFixNodeParameters( true );
9283
9284   // convert elements assigned to sub-meshes
9285   int nbCheckedElems = 0;
9286   if ( myMesh->HasShapeToMesh() )
9287   {
9288     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9289     {
9290       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9291       while ( smIt->more() ) {
9292         SMESH_subMesh* sm = smIt->next();
9293         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9294           aHelper.SetSubShape( sm->GetSubShape() );
9295           nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9296         }
9297       }
9298     }
9299   }
9300
9301   // convert elements NOT assigned to sub-meshes
9302   int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9303   if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
9304   {
9305     aHelper.SetElementsOnShape(false);
9306     SMESHDS_SubMesh *smDS = 0;
9307
9308     // convert edges
9309     SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9310     while( aEdgeItr->more() )
9311     {
9312       const SMDS_MeshEdge* edge = aEdgeItr->next();
9313       if ( !edge->IsQuadratic() )
9314       {
9315         int                  id = edge->GetID();
9316         const SMDS_MeshNode* n1 = edge->GetNode(0);
9317         const SMDS_MeshNode* n2 = edge->GetNode(1);
9318
9319         meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9320
9321         const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9322         ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9323       }
9324       else
9325       {
9326         aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
9327       }
9328     }
9329
9330     // convert faces
9331     SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9332     while( aFaceItr->more() )
9333     {
9334       const SMDS_MeshFace* face = aFaceItr->next();
9335       if ( !face ) continue;
9336       
9337       const SMDSAbs_EntityType type = face->GetEntityType();
9338       bool alreadyOK;
9339       switch( type )
9340       {
9341       case SMDSEntity_Quad_Triangle:
9342       case SMDSEntity_Quad_Quadrangle:
9343         alreadyOK = !theToBiQuad;
9344         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9345         break;
9346       case SMDSEntity_BiQuad_Triangle:
9347       case SMDSEntity_BiQuad_Quadrangle:
9348         alreadyOK = theToBiQuad;
9349         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9350         break;
9351       default: alreadyOK = false;
9352       }
9353       if ( alreadyOK )
9354         continue;
9355
9356       const int id = face->GetID();
9357       vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9358
9359       meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9360
9361       SMDS_MeshFace * NewFace = 0;
9362       switch( type )
9363       {
9364       case SMDSEntity_Triangle:
9365       case SMDSEntity_Quad_Triangle:
9366       case SMDSEntity_BiQuad_Triangle:
9367         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9368         if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
9369           GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
9370         break;
9371
9372       case SMDSEntity_Quadrangle:
9373       case SMDSEntity_Quad_Quadrangle:
9374       case SMDSEntity_BiQuad_Quadrangle:
9375         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9376         if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
9377           GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
9378         break;
9379
9380       default:;
9381         NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9382       }
9383       ReplaceElemInGroups( face, NewFace, GetMeshDS());
9384     }
9385
9386     // convert volumes
9387     vector<int> nbNodeInFaces;
9388     SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9389     while(aVolumeItr->more())
9390     {
9391       const SMDS_MeshVolume* volume = aVolumeItr->next();
9392       if ( !volume ) continue;
9393
9394       const SMDSAbs_EntityType type = volume->GetEntityType();
9395       if ( volume->IsQuadratic() )
9396       {
9397         bool alreadyOK;
9398         switch ( type )
9399         {
9400         case SMDSEntity_Quad_Hexa:    alreadyOK = !theToBiQuad; break;
9401         case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9402         default:                      alreadyOK = true;
9403         }
9404         if ( alreadyOK )
9405         {
9406           aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
9407           continue;
9408         }
9409       }
9410       const int id = volume->GetID();
9411       vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9412       if ( type == SMDSEntity_Polyhedra )
9413         nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
9414       else if ( type == SMDSEntity_Hexagonal_Prism )
9415         volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9416
9417       meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9418
9419       SMDS_MeshVolume * NewVolume = 0;
9420       switch ( type )
9421       {
9422       case SMDSEntity_Tetra:
9423         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9424         break;
9425       case SMDSEntity_Hexa:
9426       case SMDSEntity_Quad_Hexa:
9427       case SMDSEntity_TriQuad_Hexa:
9428         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9429                                       nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9430         for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
9431           if ( nodes[i]->NbInverseElements() == 0 )
9432             GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9433         break;
9434       case SMDSEntity_Pyramid:
9435         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9436                                       nodes[3], nodes[4], id, theForce3d);
9437         break;
9438       case SMDSEntity_Penta:
9439         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9440                                       nodes[3], nodes[4], nodes[5], id, theForce3d);
9441         break;
9442       case SMDSEntity_Hexagonal_Prism:
9443       default:
9444         NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9445       }
9446       ReplaceElemInGroups(volume, NewVolume, meshDS);
9447     }
9448   }
9449
9450   if ( !theForce3d )
9451   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9452     // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9453     // aHelper.FixQuadraticElements(myError);
9454     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9455   }
9456 }
9457
9458 //================================================================================
9459 /*!
9460  * \brief Makes given elements quadratic
9461  *  \param theForce3d - if true, the medium nodes will be placed in the middle of link
9462  *  \param theElements - elements to make quadratic
9463  */
9464 //================================================================================
9465
9466 void SMESH_MeshEditor::ConvertToQuadratic(const bool        theForce3d,
9467                                           TIDSortedElemSet& theElements,
9468                                           const bool        theToBiQuad)
9469 {
9470   if ( theElements.empty() ) return;
9471
9472   // we believe that all theElements are of the same type
9473   const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9474
9475   // get all nodes shared by theElements
9476   TIDSortedNodeSet allNodes;
9477   TIDSortedElemSet::iterator eIt = theElements.begin();
9478   for ( ; eIt != theElements.end(); ++eIt )
9479     allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9480
9481   // complete theElements with elements of lower dim whose all nodes are in allNodes
9482
9483   TIDSortedElemSet quadAdjacentElems    [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9484   TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9485   TIDSortedNodeSet::iterator nIt = allNodes.begin();
9486   for ( ; nIt != allNodes.end(); ++nIt )
9487   {
9488     const SMDS_MeshNode* n = *nIt;
9489     SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9490     while ( invIt->more() )
9491     {
9492       const SMDS_MeshElement*      e = invIt->next();
9493       const SMDSAbs_ElementType type = e->GetType();
9494       if ( e->IsQuadratic() )
9495       {
9496         quadAdjacentElems[ type ].insert( e );
9497
9498         bool alreadyOK;
9499         switch ( e->GetEntityType() ) {
9500         case SMDSEntity_Quad_Triangle:
9501         case SMDSEntity_Quad_Quadrangle:
9502         case SMDSEntity_Quad_Hexa:         alreadyOK = !theToBiQuad; break;
9503         case SMDSEntity_BiQuad_Triangle:
9504         case SMDSEntity_BiQuad_Quadrangle:
9505         case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; break;
9506         default:                           alreadyOK = true;
9507         }
9508         if ( alreadyOK )
9509           continue;
9510       }
9511       if ( type >= elemType )
9512         continue; // same type or more complex linear element
9513
9514       if ( !checkedAdjacentElems[ type ].insert( e ).second )
9515         continue; // e is already checked
9516
9517       // check nodes
9518       bool allIn = true;
9519       SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9520       while ( nodeIt->more() && allIn )
9521         allIn = allNodes.count( nodeIt->next() );
9522       if ( allIn )
9523         theElements.insert(e );
9524     }
9525   }
9526
9527   SMESH_MesherHelper helper(*myMesh);
9528   helper.SetIsQuadratic( true );
9529   helper.SetIsBiQuadratic( theToBiQuad );
9530
9531   // add links of quadratic adjacent elements to the helper
9532
9533   if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9534     for ( eIt  = quadAdjacentElems[SMDSAbs_Edge].begin();
9535           eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9536     {
9537       helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9538     }
9539   if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9540     for ( eIt  = quadAdjacentElems[SMDSAbs_Face].begin();
9541           eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9542     {
9543       helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9544     }
9545   if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9546     for ( eIt  = quadAdjacentElems[SMDSAbs_Volume].begin();
9547           eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9548     {
9549       helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9550     }
9551
9552   // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9553
9554   SMESHDS_Mesh*  meshDS = GetMeshDS();
9555   SMESHDS_SubMesh* smDS = 0;
9556   for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9557   {
9558     const SMDS_MeshElement* elem = *eIt;
9559
9560     bool alreadyOK;
9561     int nbCentralNodes = 0;
9562     switch ( elem->GetEntityType() ) {
9563       // linear convertible
9564     case SMDSEntity_Edge:
9565     case SMDSEntity_Triangle:
9566     case SMDSEntity_Quadrangle:
9567     case SMDSEntity_Tetra:
9568     case SMDSEntity_Pyramid:
9569     case SMDSEntity_Hexa:
9570     case SMDSEntity_Penta:             alreadyOK = false;       nbCentralNodes = 0; break;
9571       // quadratic that can become bi-quadratic
9572     case SMDSEntity_Quad_Triangle:
9573     case SMDSEntity_Quad_Quadrangle:
9574     case SMDSEntity_Quad_Hexa:         alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9575       // bi-quadratic
9576     case SMDSEntity_BiQuad_Triangle:
9577     case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9578     case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9579       // the rest
9580     default:                           alreadyOK = true;
9581     }
9582     if ( alreadyOK ) continue;
9583
9584     const SMDSAbs_ElementType type = elem->GetType();
9585     const int                   id = elem->GetID();
9586     const int              nbNodes = elem->NbCornerNodes();
9587     vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9588
9589     helper.SetSubShape( elem->getshapeId() );
9590
9591     if ( !smDS || !smDS->Contains( elem ))
9592       smDS = meshDS->MeshElements( elem->getshapeId() );
9593     meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9594
9595     SMDS_MeshElement * newElem = 0;
9596     switch( nbNodes )
9597     {
9598     case 4: // cases for most frequently used element types go first (for optimization)
9599       if ( type == SMDSAbs_Volume )
9600         newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9601       else
9602         newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9603       break;
9604     case 8:
9605       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9606                                  nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9607       break;
9608     case 3:
9609       newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], id, theForce3d);
9610       break;
9611     case 2:
9612       newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9613       break;
9614     case 5:
9615       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9616                                  nodes[4], id, theForce3d);
9617       break;
9618     case 6:
9619       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9620                                  nodes[4], nodes[5], id, theForce3d);
9621       break;
9622     default:;
9623     }
9624     ReplaceElemInGroups( elem, newElem, meshDS);
9625     if( newElem && smDS )
9626       smDS->AddElement( newElem );
9627
9628      // remove central nodes
9629     for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9630       if ( nodes[i]->NbInverseElements() == 0 )
9631         meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9632
9633   } // loop on theElements
9634
9635   if ( !theForce3d )
9636   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9637     // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9638     // helper.FixQuadraticElements( myError );
9639     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9640   }
9641 }
9642
9643 //=======================================================================
9644 /*!
9645  * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9646  * \return int - nb of checked elements
9647  */
9648 //=======================================================================
9649
9650 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh *    theSm,
9651                                      SMDS_ElemIteratorPtr theItr,
9652                                      const int            theShapeID)
9653 {
9654   int nbElem = 0;
9655   SMESHDS_Mesh* meshDS = GetMeshDS();
9656   ElemFeatures elemType;
9657   vector<const SMDS_MeshNode *> nodes;
9658
9659   while( theItr->more() )
9660   {
9661     const SMDS_MeshElement* elem = theItr->next();
9662     nbElem++;
9663     if( elem && elem->IsQuadratic())
9664     {
9665       // get elem data
9666       int nbCornerNodes = elem->NbCornerNodes();
9667       nodes.assign( elem->begin_nodes(), elem->end_nodes() );
9668
9669       elemType.Init( elem, /*basicOnly=*/false ).SetID( elem->GetID() ).SetQuad( false );
9670
9671       //remove a quadratic element
9672       if ( !theSm || !theSm->Contains( elem ))
9673         theSm = meshDS->MeshElements( elem->getshapeId() );
9674       meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9675
9676       // remove medium nodes
9677       for ( size_t i = nbCornerNodes; i < nodes.size(); ++i )
9678         if ( nodes[i]->NbInverseElements() == 0 )
9679           meshDS->RemoveFreeNode( nodes[i], theSm );
9680
9681       // add a linear element
9682       nodes.resize( nbCornerNodes );
9683       SMDS_MeshElement * newElem = AddElement( nodes, elemType );
9684       ReplaceElemInGroups(elem, newElem, meshDS);
9685       if( theSm && newElem )
9686         theSm->AddElement( newElem );
9687     }
9688   }
9689   return nbElem;
9690 }
9691
9692 //=======================================================================
9693 //function : ConvertFromQuadratic
9694 //purpose  :
9695 //=======================================================================
9696
9697 bool SMESH_MeshEditor::ConvertFromQuadratic()
9698 {
9699   int nbCheckedElems = 0;
9700   if ( myMesh->HasShapeToMesh() )
9701   {
9702     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9703     {
9704       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9705       while ( smIt->more() ) {
9706         SMESH_subMesh* sm = smIt->next();
9707         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9708           nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9709       }
9710     }
9711   }
9712
9713   int totalNbElems =
9714     GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9715   if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9716   {
9717     SMESHDS_SubMesh *aSM = 0;
9718     removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9719   }
9720
9721   return true;
9722 }
9723
9724 namespace
9725 {
9726   //================================================================================
9727   /*!
9728    * \brief Return true if all medium nodes of the element are in the node set
9729    */
9730   //================================================================================
9731
9732   bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9733   {
9734     for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9735       if ( !nodeSet.count( elem->GetNode(i) ))
9736         return false;
9737     return true;
9738   }
9739 }
9740
9741 //================================================================================
9742 /*!
9743  * \brief Makes given elements linear
9744  */
9745 //================================================================================
9746
9747 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9748 {
9749   if ( theElements.empty() ) return;
9750
9751   // collect IDs of medium nodes of theElements; some of these nodes will be removed
9752   set<int> mediumNodeIDs;
9753   TIDSortedElemSet::iterator eIt = theElements.begin();
9754   for ( ; eIt != theElements.end(); ++eIt )
9755   {
9756     const SMDS_MeshElement* e = *eIt;
9757     for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9758       mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9759   }
9760
9761   // replace given elements by linear ones
9762   SMDS_ElemIteratorPtr elemIt = elemSetIterator( theElements );
9763   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9764
9765   // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9766   // except those elements sharing medium nodes of quadratic element whose medium nodes
9767   // are not all in mediumNodeIDs
9768
9769   // get remaining medium nodes
9770   TIDSortedNodeSet mediumNodes;
9771   set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9772   for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9773     if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9774       mediumNodes.insert( mediumNodes.end(), n );
9775
9776   // find more quadratic elements to convert
9777   TIDSortedElemSet moreElemsToConvert;
9778   TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9779   for ( ; nIt != mediumNodes.end(); ++nIt )
9780   {
9781     SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9782     while ( invIt->more() )
9783     {
9784       const SMDS_MeshElement* e = invIt->next();
9785       if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9786       {
9787         // find a more complex element including e and
9788         // whose medium nodes are not in mediumNodes
9789         bool complexFound = false;
9790         for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9791         {
9792           SMDS_ElemIteratorPtr invIt2 =
9793             (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9794           while ( invIt2->more() )
9795           {
9796             const SMDS_MeshElement* eComplex = invIt2->next();
9797             if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9798             {
9799               int nbCommonNodes = SMESH_MeshAlgos::GetCommonNodes( e, eComplex ).size();
9800               if ( nbCommonNodes == e->NbNodes())
9801               {
9802                 complexFound = true;
9803                 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9804                 break;
9805               }
9806             }
9807           }
9808         }
9809         if ( !complexFound )
9810           moreElemsToConvert.insert( e );
9811       }
9812     }
9813   }
9814   elemIt = elemSetIterator( moreElemsToConvert );
9815   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9816 }
9817
9818 //=======================================================================
9819 //function : SewSideElements
9820 //purpose  :
9821 //=======================================================================
9822
9823 SMESH_MeshEditor::Sew_Error
9824 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet&    theSide1,
9825                                    TIDSortedElemSet&    theSide2,
9826                                    const SMDS_MeshNode* theFirstNode1,
9827                                    const SMDS_MeshNode* theFirstNode2,
9828                                    const SMDS_MeshNode* theSecondNode1,
9829                                    const SMDS_MeshNode* theSecondNode2)
9830 {
9831   myLastCreatedElems.Clear();
9832   myLastCreatedNodes.Clear();
9833
9834   if ( theSide1.size() != theSide2.size() )
9835     return SEW_DIFF_NB_OF_ELEMENTS;
9836
9837   Sew_Error aResult = SEW_OK;
9838   // Algo:
9839   // 1. Build set of faces representing each side
9840   // 2. Find which nodes of the side 1 to merge with ones on the side 2
9841   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9842
9843   // =======================================================================
9844   // 1. Build set of faces representing each side:
9845   // =======================================================================
9846   // a. build set of nodes belonging to faces
9847   // b. complete set of faces: find missing faces whose nodes are in set of nodes
9848   // c. create temporary faces representing side of volumes if correspondent
9849   //    face does not exist
9850
9851   SMESHDS_Mesh* aMesh = GetMeshDS();
9852   // TODO algoritm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9853   //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9854   TIDSortedElemSet             faceSet1, faceSet2;
9855   set<const SMDS_MeshElement*> volSet1,  volSet2;
9856   set<const SMDS_MeshNode*>    nodeSet1, nodeSet2;
9857   TIDSortedElemSet             * faceSetPtr[] = { &faceSet1, &faceSet2 };
9858   set<const SMDS_MeshElement*> *  volSetPtr[] = { &volSet1,  &volSet2  };
9859   set<const SMDS_MeshNode*>    * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9860   TIDSortedElemSet             * elemSetPtr[] = { &theSide1, &theSide2 };
9861   int iSide, iFace, iNode;
9862
9863   list<const SMDS_MeshElement* > tempFaceList;
9864   for ( iSide = 0; iSide < 2; iSide++ ) {
9865     set<const SMDS_MeshNode*>    * nodeSet = nodeSetPtr[ iSide ];
9866     TIDSortedElemSet             * elemSet = elemSetPtr[ iSide ];
9867     TIDSortedElemSet             * faceSet = faceSetPtr[ iSide ];
9868     set<const SMDS_MeshElement*> * volSet  = volSetPtr [ iSide ];
9869     set<const SMDS_MeshElement*>::iterator vIt;
9870     TIDSortedElemSet::iterator eIt;
9871     set<const SMDS_MeshNode*>::iterator    nIt;
9872
9873     // check that given nodes belong to given elements
9874     const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9875     const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9876     int firstIndex = -1, secondIndex = -1;
9877     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9878       const SMDS_MeshElement* elem = *eIt;
9879       if ( firstIndex  < 0 ) firstIndex  = elem->GetNodeIndex( n1 );
9880       if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9881       if ( firstIndex > -1 && secondIndex > -1 ) break;
9882     }
9883     if ( firstIndex < 0 || secondIndex < 0 ) {
9884       // we can simply return until temporary faces created
9885       return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9886     }
9887
9888     // -----------------------------------------------------------
9889     // 1a. Collect nodes of existing faces
9890     //     and build set of face nodes in order to detect missing
9891     //     faces corresponding to sides of volumes
9892     // -----------------------------------------------------------
9893
9894     set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9895
9896     // loop on the given element of a side
9897     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9898       //const SMDS_MeshElement* elem = *eIt;
9899       const SMDS_MeshElement* elem = *eIt;
9900       if ( elem->GetType() == SMDSAbs_Face ) {
9901         faceSet->insert( elem );
9902         set <const SMDS_MeshNode*> faceNodeSet;
9903         SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9904         while ( nodeIt->more() ) {
9905           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9906           nodeSet->insert( n );
9907           faceNodeSet.insert( n );
9908         }
9909         setOfFaceNodeSet.insert( faceNodeSet );
9910       }
9911       else if ( elem->GetType() == SMDSAbs_Volume )
9912         volSet->insert( elem );
9913     }
9914     // ------------------------------------------------------------------------------
9915     // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9916     // ------------------------------------------------------------------------------
9917
9918     for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9919       SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9920       while ( fIt->more() ) { // loop on faces sharing a node
9921         const SMDS_MeshElement* f = fIt->next();
9922         if ( faceSet->find( f ) == faceSet->end() ) {
9923           // check if all nodes are in nodeSet and
9924           // complete setOfFaceNodeSet if they are
9925           set <const SMDS_MeshNode*> faceNodeSet;
9926           SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9927           bool allInSet = true;
9928           while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9929             const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9930             if ( nodeSet->find( n ) == nodeSet->end() )
9931               allInSet = false;
9932             else
9933               faceNodeSet.insert( n );
9934           }
9935           if ( allInSet ) {
9936             faceSet->insert( f );
9937             setOfFaceNodeSet.insert( faceNodeSet );
9938           }
9939         }
9940       }
9941     }
9942
9943     // -------------------------------------------------------------------------
9944     // 1c. Create temporary faces representing sides of volumes if correspondent
9945     //     face does not exist
9946     // -------------------------------------------------------------------------
9947
9948     if ( !volSet->empty() ) {
9949       //int nodeSetSize = nodeSet->size();
9950
9951       // loop on given volumes
9952       for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
9953         SMDS_VolumeTool vol (*vIt);
9954         // loop on volume faces: find free faces
9955         // --------------------------------------
9956         list<const SMDS_MeshElement* > freeFaceList;
9957         for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
9958           if ( !vol.IsFreeFace( iFace ))
9959             continue;
9960           // check if there is already a face with same nodes in a face set
9961           const SMDS_MeshElement* aFreeFace = 0;
9962           const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
9963           int nbNodes = vol.NbFaceNodes( iFace );
9964           set <const SMDS_MeshNode*> faceNodeSet;
9965           vol.GetFaceNodes( iFace, faceNodeSet );
9966           bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
9967           if ( isNewFace ) {
9968             // no such a face is given but it still can exist, check it
9969             vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
9970             aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
9971           }
9972           if ( !aFreeFace ) {
9973             // create a temporary face
9974             if ( nbNodes == 3 ) {
9975               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
9976               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
9977             }
9978             else if ( nbNodes == 4 ) {
9979               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9980               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9981             }
9982             else {
9983               vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9984               //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
9985               aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
9986             }
9987             if ( aFreeFace )
9988               tempFaceList.push_back( aFreeFace );
9989           }
9990
9991           if ( aFreeFace )
9992             freeFaceList.push_back( aFreeFace );
9993
9994         } // loop on faces of a volume
9995
9996         // choose one of several free faces of a volume
9997         // --------------------------------------------
9998         if ( freeFaceList.size() > 1 ) {
9999           // choose a face having max nb of nodes shared by other elems of a side
10000           int maxNbNodes = -1;
10001           list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
10002           while ( fIt != freeFaceList.end() ) { // loop on free faces
10003             int nbSharedNodes = 0;
10004             SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10005             while ( nodeIt->more() ) { // loop on free face nodes
10006               const SMDS_MeshNode* n =
10007                 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10008               SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
10009               while ( invElemIt->more() ) {
10010                 const SMDS_MeshElement* e = invElemIt->next();
10011                 nbSharedNodes += faceSet->count( e );
10012                 nbSharedNodes += elemSet->count( e );
10013               }
10014             }
10015             if ( nbSharedNodes > maxNbNodes ) {
10016               maxNbNodes = nbSharedNodes;
10017               freeFaceList.erase( freeFaceList.begin(), fIt++ );
10018             }
10019             else if ( nbSharedNodes == maxNbNodes ) {
10020               fIt++;
10021             }
10022             else {
10023               freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
10024             }
10025           }
10026           if ( freeFaceList.size() > 1 )
10027           {
10028             // could not choose one face, use another way
10029             // choose a face most close to the bary center of the opposite side
10030             gp_XYZ aBC( 0., 0., 0. );
10031             set <const SMDS_MeshNode*> addedNodes;
10032             TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
10033             eIt = elemSet2->begin();
10034             for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
10035               SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
10036               while ( nodeIt->more() ) { // loop on free face nodes
10037                 const SMDS_MeshNode* n =
10038                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10039                 if ( addedNodes.insert( n ).second )
10040                   aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
10041               }
10042             }
10043             aBC /= addedNodes.size();
10044             double minDist = DBL_MAX;
10045             fIt = freeFaceList.begin();
10046             while ( fIt != freeFaceList.end() ) { // loop on free faces
10047               double dist = 0;
10048               SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10049               while ( nodeIt->more() ) { // loop on free face nodes
10050                 const SMDS_MeshNode* n =
10051                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10052                 gp_XYZ p( n->X(),n->Y(),n->Z() );
10053                 dist += ( aBC - p ).SquareModulus();
10054               }
10055               if ( dist < minDist ) {
10056                 minDist = dist;
10057                 freeFaceList.erase( freeFaceList.begin(), fIt++ );
10058               }
10059               else
10060                 fIt = freeFaceList.erase( fIt++ );
10061             }
10062           }
10063         } // choose one of several free faces of a volume
10064
10065         if ( freeFaceList.size() == 1 ) {
10066           const SMDS_MeshElement* aFreeFace = freeFaceList.front();
10067           faceSet->insert( aFreeFace );
10068           // complete a node set with nodes of a found free face
10069           //           for ( iNode = 0; iNode < ; iNode++ )
10070           //             nodeSet->insert( fNodes[ iNode ] );
10071         }
10072
10073       } // loop on volumes of a side
10074
10075       //       // complete a set of faces if new nodes in a nodeSet appeared
10076       //       // ----------------------------------------------------------
10077       //       if ( nodeSetSize != nodeSet->size() ) {
10078       //         for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10079       //           SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10080       //           while ( fIt->more() ) { // loop on faces sharing a node
10081       //             const SMDS_MeshElement* f = fIt->next();
10082       //             if ( faceSet->find( f ) == faceSet->end() ) {
10083       //               // check if all nodes are in nodeSet and
10084       //               // complete setOfFaceNodeSet if they are
10085       //               set <const SMDS_MeshNode*> faceNodeSet;
10086       //               SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10087       //               bool allInSet = true;
10088       //               while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10089       //                 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10090       //                 if ( nodeSet->find( n ) == nodeSet->end() )
10091       //                   allInSet = false;
10092       //                 else
10093       //                   faceNodeSet.insert( n );
10094       //               }
10095       //               if ( allInSet ) {
10096       //                 faceSet->insert( f );
10097       //                 setOfFaceNodeSet.insert( faceNodeSet );
10098       //               }
10099       //             }
10100       //           }
10101       //         }
10102       //       }
10103     } // Create temporary faces, if there are volumes given
10104   } // loop on sides
10105
10106   if ( faceSet1.size() != faceSet2.size() ) {
10107     // delete temporary faces: they are in reverseElements of actual nodes
10108 //    SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10109 //    while ( tmpFaceIt->more() )
10110 //      aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10111 //    list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10112 //    for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10113 //      aMesh->RemoveElement(*tmpFaceIt);
10114     MESSAGE("Diff nb of faces");
10115     return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10116   }
10117
10118   // ============================================================
10119   // 2. Find nodes to merge:
10120   //              bind a node to remove to a node to put instead
10121   // ============================================================
10122
10123   TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
10124   if ( theFirstNode1 != theFirstNode2 )
10125     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10126   if ( theSecondNode1 != theSecondNode2 )
10127     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10128
10129   LinkID_Gen aLinkID_Gen( GetMeshDS() );
10130   set< long > linkIdSet; // links to process
10131   linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
10132
10133   typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
10134   list< NLink > linkList[2];
10135   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10136   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10137   // loop on links in linkList; find faces by links and append links
10138   // of the found faces to linkList
10139   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10140   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
10141   {
10142     NLink link[] = { *linkIt[0], *linkIt[1] };
10143     long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
10144     if ( !linkIdSet.count( linkID ) )
10145       continue;
10146
10147     // by links, find faces in the face sets,
10148     // and find indices of link nodes in the found faces;
10149     // in a face set, there is only one or no face sharing a link
10150     // ---------------------------------------------------------------
10151
10152     const SMDS_MeshElement* face[] = { 0, 0 };
10153     vector<const SMDS_MeshNode*> fnodes[2];
10154     int iLinkNode[2][2];
10155     TIDSortedElemSet avoidSet;
10156     for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10157       const SMDS_MeshNode* n1 = link[iSide].first;
10158       const SMDS_MeshNode* n2 = link[iSide].second;
10159       //cout << "Side " << iSide << " ";
10160       //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
10161       // find a face by two link nodes
10162       face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
10163                                                       *faceSetPtr[ iSide ], avoidSet,
10164                                                       &iLinkNode[iSide][0],
10165                                                       &iLinkNode[iSide][1] );
10166       if ( face[ iSide ])
10167       {
10168         //cout << " F " << face[ iSide]->GetID() <<endl;
10169         faceSetPtr[ iSide ]->erase( face[ iSide ]);
10170         // put face nodes to fnodes
10171         if ( face[ iSide ]->IsQuadratic() )
10172         {
10173           // use interlaced nodes iterator
10174           const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>( face[ iSide ]);
10175           if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
10176           SMDS_ElemIteratorPtr nIter = F->interlacedNodesElemIterator();
10177           while ( nIter->more() )
10178             fnodes[ iSide ].push_back( cast2Node( nIter->next() ));
10179         }
10180         else
10181         {
10182           fnodes[ iSide ].assign( face[ iSide ]->begin_nodes(),
10183                                   face[ iSide ]->end_nodes() );
10184         }
10185         fnodes[ iSide ].push_back( fnodes[ iSide ].front());
10186       }
10187     }
10188
10189     // check similarity of elements of the sides
10190     if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10191       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10192       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10193         aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10194       }
10195       else {
10196         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10197       }
10198       break; // do not return because it's necessary to remove tmp faces
10199     }
10200
10201     // set nodes to merge
10202     // -------------------
10203
10204     if ( face[0] && face[1] )  {
10205       const int nbNodes = face[0]->NbNodes();
10206       if ( nbNodes != face[1]->NbNodes() ) {
10207         MESSAGE("Diff nb of face nodes");
10208         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10209         break; // do not return because it s necessary to remove tmp faces
10210       }
10211       bool reverse[] = { false, false }; // order of nodes in the link
10212       for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10213         // analyse link orientation in faces
10214         int i1 = iLinkNode[ iSide ][ 0 ];
10215         int i2 = iLinkNode[ iSide ][ 1 ];
10216         reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10217       }
10218       int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
10219       int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
10220       for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
10221       {
10222         nReplaceMap.insert  ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
10223                                           fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
10224       }
10225
10226       // add other links of the faces to linkList
10227       // -----------------------------------------
10228
10229       for ( iNode = 0; iNode < nbNodes; iNode++ )  {
10230         linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
10231         pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10232         if ( !iter_isnew.second ) { // already in a set: no need to process
10233           linkIdSet.erase( iter_isnew.first );
10234         }
10235         else // new in set == encountered for the first time: add
10236         {
10237           const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
10238           const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
10239           linkList[0].push_back ( NLink( n1, n2 ));
10240           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10241         }
10242       }
10243     } // 2 faces found
10244
10245     if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
10246       break;
10247
10248   } // loop on link lists
10249
10250   if ( aResult == SEW_OK &&
10251        ( //linkIt[0] != linkList[0].end() ||
10252          !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10253     MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10254              " " << (faceSetPtr[1]->empty()));
10255     aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10256   }
10257
10258   // ====================================================================
10259   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10260   // ====================================================================
10261
10262   // delete temporary faces
10263 //  SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10264 //  while ( tmpFaceIt->more() )
10265 //    aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10266   list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10267   for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10268     aMesh->RemoveElement(*tmpFaceIt);
10269
10270   if ( aResult != SEW_OK)
10271     return aResult;
10272
10273   list< int > nodeIDsToRemove;
10274   vector< const SMDS_MeshNode*> nodes;
10275   ElemFeatures elemType;
10276
10277   // loop on nodes replacement map
10278   TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10279   for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10280     if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second )
10281     {
10282       const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10283       nodeIDsToRemove.push_back( nToRemove->GetID() );
10284       // loop on elements sharing nToRemove
10285       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10286       while ( invElemIt->more() ) {
10287         const SMDS_MeshElement* e = invElemIt->next();
10288         // get a new suite of nodes: make replacement
10289         int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10290         nodes.resize( nbNodes );
10291         SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10292         while ( nIt->more() ) {
10293           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
10294           nnIt = nReplaceMap.find( n );
10295           if ( nnIt != nReplaceMap.end() ) {
10296             nbReplaced++;
10297             n = (*nnIt).second;
10298           }
10299           nodes[ i++ ] = n;
10300         }
10301         //       if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10302         //         elemIDsToRemove.push_back( e->GetID() );
10303         //       else
10304         if ( nbReplaced )
10305         {
10306           elemType.Init( e, /*basicOnly=*/false ).SetID( e->GetID() );
10307           aMesh->RemoveElement( e );
10308
10309           if ( SMDS_MeshElement* newElem = this->AddElement( nodes, elemType ))
10310           {
10311             AddToSameGroups( newElem, e, aMesh );
10312             if ( int aShapeId = e->getshapeId() )
10313               aMesh->SetMeshElementOnShape( newElem, aShapeId );
10314           }
10315         }
10316       }
10317     }
10318
10319   Remove( nodeIDsToRemove, true );
10320
10321   return aResult;
10322 }
10323
10324 //================================================================================
10325 /*!
10326  * \brief Find corresponding nodes in two sets of faces
10327  * \param theSide1 - first face set
10328  * \param theSide2 - second first face
10329  * \param theFirstNode1 - a boundary node of set 1
10330  * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10331  * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10332  * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10333  * \param nReplaceMap - output map of corresponding nodes
10334  * \return bool  - is a success or not
10335  */
10336 //================================================================================
10337
10338 #ifdef _DEBUG_
10339 //#define DEBUG_MATCHING_NODES
10340 #endif
10341
10342 SMESH_MeshEditor::Sew_Error
10343 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10344                                     set<const SMDS_MeshElement*>& theSide2,
10345                                     const SMDS_MeshNode*          theFirstNode1,
10346                                     const SMDS_MeshNode*          theFirstNode2,
10347                                     const SMDS_MeshNode*          theSecondNode1,
10348                                     const SMDS_MeshNode*          theSecondNode2,
10349                                     TNodeNodeMap &                nReplaceMap)
10350 {
10351   set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10352
10353   nReplaceMap.clear();
10354   if ( theFirstNode1 != theFirstNode2 )
10355     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10356   if ( theSecondNode1 != theSecondNode2 )
10357     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10358
10359   set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10360   linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10361
10362   list< NLink > linkList[2];
10363   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10364   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10365
10366   // loop on links in linkList; find faces by links and append links
10367   // of the found faces to linkList
10368   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10369   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10370     NLink link[] = { *linkIt[0], *linkIt[1] };
10371     if ( linkSet.find( link[0] ) == linkSet.end() )
10372       continue;
10373
10374     // by links, find faces in the face sets,
10375     // and find indices of link nodes in the found faces;
10376     // in a face set, there is only one or no face sharing a link
10377     // ---------------------------------------------------------------
10378
10379     const SMDS_MeshElement* face[] = { 0, 0 };
10380     list<const SMDS_MeshNode*> notLinkNodes[2];
10381     //bool reverse[] = { false, false }; // order of notLinkNodes
10382     int nbNodes[2];
10383     for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10384     {
10385       const SMDS_MeshNode* n1 = link[iSide].first;
10386       const SMDS_MeshNode* n2 = link[iSide].second;
10387       set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10388       set< const SMDS_MeshElement* > facesOfNode1;
10389       for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10390       {
10391         // during a loop of the first node, we find all faces around n1,
10392         // during a loop of the second node, we find one face sharing both n1 and n2
10393         const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10394         SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10395         while ( fIt->more() ) { // loop on faces sharing a node
10396           const SMDS_MeshElement* f = fIt->next();
10397           if (faceSet->find( f ) != faceSet->end() && // f is in face set
10398               ! facesOfNode1.insert( f ).second ) // f encounters twice
10399           {
10400             if ( face[ iSide ] ) {
10401               MESSAGE( "2 faces per link " );
10402               return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10403             }
10404             face[ iSide ] = f;
10405             faceSet->erase( f );
10406
10407             // get not link nodes
10408             int nbN = f->NbNodes();
10409             if ( f->IsQuadratic() )
10410               nbN /= 2;
10411             nbNodes[ iSide ] = nbN;
10412             list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10413             int i1 = f->GetNodeIndex( n1 );
10414             int i2 = f->GetNodeIndex( n2 );
10415             int iEnd = nbN, iBeg = -1, iDelta = 1;
10416             bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10417             if ( reverse ) {
10418               std::swap( iEnd, iBeg ); iDelta = -1;
10419             }
10420             int i = i2;
10421             while ( true ) {
10422               i += iDelta;
10423               if ( i == iEnd ) i = iBeg + iDelta;
10424               if ( i == i1 ) break;
10425               nodes.push_back ( f->GetNode( i ) );
10426             }
10427           }
10428         }
10429       }
10430     }
10431     // check similarity of elements of the sides
10432     if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10433       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10434       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10435         return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10436       }
10437       else {
10438         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10439       }
10440     }
10441
10442     // set nodes to merge
10443     // -------------------
10444
10445     if ( face[0] && face[1] )  {
10446       if ( nbNodes[0] != nbNodes[1] ) {
10447         MESSAGE("Diff nb of face nodes");
10448         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10449       }
10450 #ifdef DEBUG_MATCHING_NODES
10451       MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10452                 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10453                 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10454 #endif
10455       int nbN = nbNodes[0];
10456       {
10457         list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10458         list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10459         for ( int i = 0 ; i < nbN - 2; ++i ) {
10460 #ifdef DEBUG_MATCHING_NODES
10461           MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10462 #endif
10463           nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10464         }
10465       }
10466
10467       // add other links of the face 1 to linkList
10468       // -----------------------------------------
10469
10470       const SMDS_MeshElement* f0 = face[0];
10471       const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10472       for ( int i = 0; i < nbN; i++ )
10473       {
10474         const SMDS_MeshNode* n2 = f0->GetNode( i );
10475         pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10476           linkSet.insert( SMESH_TLink( n1, n2 ));
10477         if ( !iter_isnew.second ) { // already in a set: no need to process
10478           linkSet.erase( iter_isnew.first );
10479         }
10480         else // new in set == encountered for the first time: add
10481         {
10482 #ifdef DEBUG_MATCHING_NODES
10483           MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10484                     << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10485 #endif
10486           linkList[0].push_back ( NLink( n1, n2 ));
10487           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10488         }
10489         n1 = n2;
10490       }
10491     } // 2 faces found
10492   } // loop on link lists
10493
10494   return SEW_OK;
10495 }
10496
10497 //================================================================================
10498 /*!
10499  * \brief Create elements equal (on same nodes) to given ones
10500  *  \param [in] theElements - a set of elems to duplicate. If it is empty, all
10501  *              elements of the uppest dimension are duplicated.
10502  */
10503 //================================================================================
10504
10505 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
10506 {
10507   ClearLastCreated();
10508   SMESHDS_Mesh* mesh = GetMeshDS();
10509
10510   // get an element type and an iterator over elements
10511
10512   SMDSAbs_ElementType type = SMDSAbs_All;
10513   SMDS_ElemIteratorPtr elemIt;
10514   vector< const SMDS_MeshElement* > allElems;
10515   if ( theElements.empty() )
10516   {
10517     if ( mesh->NbNodes() == 0 )
10518       return;
10519     // get most complex type
10520     SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10521       SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10522       SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10523     };
10524     for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10525       if ( mesh->GetMeshInfo().NbElements( types[i] ))
10526       {
10527         type = types[i];
10528         break;
10529       }
10530     // put all elements in the vector <allElems>
10531     allElems.reserve( mesh->GetMeshInfo().NbElements( type ));
10532     elemIt = mesh->elementsIterator( type );
10533     while ( elemIt->more() )
10534       allElems.push_back( elemIt->next());
10535     elemIt = elemSetIterator( allElems );
10536   }
10537   else
10538   {
10539     type = (*theElements.begin())->GetType();
10540     elemIt = elemSetIterator( theElements );
10541   }
10542
10543   // duplicate elements
10544
10545   ElemFeatures elemType;
10546
10547   vector< const SMDS_MeshNode* > nodes;
10548   while ( elemIt->more() )
10549   {
10550     const SMDS_MeshElement* elem = elemIt->next();
10551     if ( elem->GetType() != type )
10552       continue;
10553
10554     elemType.Init( elem, /*basicOnly=*/false );
10555     nodes.assign( elem->begin_nodes(), elem->end_nodes() );
10556
10557     AddElement( nodes, elemType );
10558   }
10559 }
10560
10561 //================================================================================
10562 /*!
10563   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10564   \param theElems - the list of elements (edges or faces) to be replicated
10565   The nodes for duplication could be found from these elements
10566   \param theNodesNot - list of nodes to NOT replicate
10567   \param theAffectedElems - the list of elements (cells and edges) to which the
10568   replicated nodes should be associated to.
10569   \return TRUE if operation has been completed successfully, FALSE otherwise
10570 */
10571 //================================================================================
10572
10573 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10574                                     const TIDSortedElemSet& theNodesNot,
10575                                     const TIDSortedElemSet& theAffectedElems )
10576 {
10577   myLastCreatedElems.Clear();
10578   myLastCreatedNodes.Clear();
10579
10580   if ( theElems.size() == 0 )
10581     return false;
10582
10583   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10584   if ( !aMeshDS )
10585     return false;
10586
10587   bool res = false;
10588   TNodeNodeMap anOldNodeToNewNode;
10589   // duplicate elements and nodes
10590   res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10591   // replce nodes by duplications
10592   res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10593   return res;
10594 }
10595
10596 //================================================================================
10597 /*!
10598   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10599   \param theMeshDS - mesh instance
10600   \param theElems - the elements replicated or modified (nodes should be changed)
10601   \param theNodesNot - nodes to NOT replicate
10602   \param theNodeNodeMap - relation of old node to new created node
10603   \param theIsDoubleElem - flag os to replicate element or modify
10604   \return TRUE if operation has been completed successfully, FALSE otherwise
10605 */
10606 //================================================================================
10607
10608 bool SMESH_MeshEditor::doubleNodes(SMESHDS_Mesh*           theMeshDS,
10609                                    const TIDSortedElemSet& theElems,
10610                                    const TIDSortedElemSet& theNodesNot,
10611                                    TNodeNodeMap&           theNodeNodeMap,
10612                                    const bool              theIsDoubleElem )
10613 {
10614   // iterate through element and duplicate them (by nodes duplication)
10615   bool res = false;
10616   std::vector<const SMDS_MeshNode*> newNodes;
10617   ElemFeatures elemType;
10618
10619   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10620   for ( ;  elemItr != theElems.end(); ++elemItr )
10621   {
10622     const SMDS_MeshElement* anElem = *elemItr;
10623     if (!anElem)
10624       continue;
10625
10626     // duplicate nodes to duplicate element
10627     bool isDuplicate = false;
10628     newNodes.resize( anElem->NbNodes() );
10629     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10630     int ind = 0;
10631     while ( anIter->more() )
10632     {
10633       const SMDS_MeshNode* aCurrNode = static_cast<const SMDS_MeshNode*>( anIter->next() );
10634       const SMDS_MeshNode*  aNewNode = aCurrNode;
10635       TNodeNodeMap::iterator     n2n = theNodeNodeMap.find( aCurrNode );
10636       if ( n2n != theNodeNodeMap.end() )
10637       {
10638         aNewNode = n2n->second;
10639       }
10640       else if ( theIsDoubleElem && !theNodesNot.count( aCurrNode ))
10641       {
10642         // duplicate node
10643         aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10644         copyPosition( aCurrNode, aNewNode );
10645         theNodeNodeMap[ aCurrNode ] = aNewNode;
10646         myLastCreatedNodes.Append( aNewNode );
10647       }
10648       isDuplicate |= (aCurrNode != aNewNode);
10649       newNodes[ ind++ ] = aNewNode;
10650     }
10651     if ( !isDuplicate )
10652       continue;
10653
10654     if ( theIsDoubleElem )
10655       AddElement( newNodes, elemType.Init( anElem, /*basicOnly=*/false ));
10656     else
10657       theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], newNodes.size() );
10658
10659     res = true;
10660   }
10661   return res;
10662 }
10663
10664 //================================================================================
10665 /*!
10666   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10667   \param theNodes - identifiers of nodes to be doubled
10668   \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10669   nodes. If list of element identifiers is empty then nodes are doubled but
10670   they not assigned to elements
10671   \return TRUE if operation has been completed successfully, FALSE otherwise
10672 */
10673 //================================================================================
10674
10675 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10676                                     const std::list< int >& theListOfModifiedElems )
10677 {
10678   myLastCreatedElems.Clear();
10679   myLastCreatedNodes.Clear();
10680
10681   if ( theListOfNodes.size() == 0 )
10682     return false;
10683
10684   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10685   if ( !aMeshDS )
10686     return false;
10687
10688   // iterate through nodes and duplicate them
10689
10690   std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10691
10692   std::list< int >::const_iterator aNodeIter;
10693   for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10694   {
10695     int aCurr = *aNodeIter;
10696     SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr );
10697     if ( !aNode )
10698       continue;
10699
10700     // duplicate node
10701
10702     const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10703     if ( aNewNode )
10704     {
10705       copyPosition( aNode, aNewNode );
10706       anOldNodeToNewNode[ aNode ] = aNewNode;
10707       myLastCreatedNodes.Append( aNewNode );
10708     }
10709   }
10710
10711   // Create map of new nodes for modified elements
10712
10713   std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> > anElemToNodes;
10714
10715   std::list< int >::const_iterator anElemIter;
10716   for ( anElemIter = theListOfModifiedElems.begin();
10717         anElemIter != theListOfModifiedElems.end(); ++anElemIter )
10718   {
10719     int aCurr = *anElemIter;
10720     SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr );
10721     if ( !anElem )
10722       continue;
10723
10724     vector<const SMDS_MeshNode*> aNodeArr( anElem->NbNodes() );
10725
10726     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10727     int ind = 0;
10728     while ( anIter->more() )
10729     {
10730       SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10731       if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() )
10732       {
10733         const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ];
10734         aNodeArr[ ind++ ] = aNewNode;
10735       }
10736       else
10737         aNodeArr[ ind++ ] = aCurrNode;
10738     }
10739     anElemToNodes[ anElem ] = aNodeArr;
10740   }
10741
10742   // Change nodes of elements
10743
10744   std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> >::iterator
10745     anElemToNodesIter = anElemToNodes.begin();
10746   for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter )
10747   {
10748     const SMDS_MeshElement* anElem = anElemToNodesIter->first;
10749     vector<const SMDS_MeshNode*> aNodeArr = anElemToNodesIter->second;
10750     if ( anElem )
10751     {
10752       aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() );
10753     }
10754   }
10755
10756   return true;
10757 }
10758
10759 namespace {
10760
10761   //================================================================================
10762   /*!
10763   \brief Check if element located inside shape
10764   \return TRUE if IN or ON shape, FALSE otherwise
10765   */
10766   //================================================================================
10767
10768   template<class Classifier>
10769   bool isInside(const SMDS_MeshElement* theElem,
10770                 Classifier&             theClassifier,
10771                 const double            theTol)
10772   {
10773     gp_XYZ centerXYZ (0, 0, 0);
10774     SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
10775     while (aNodeItr->more())
10776       centerXYZ += SMESH_TNodeXYZ(cast2Node( aNodeItr->next()));
10777
10778     gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10779     theClassifier.Perform(aPnt, theTol);
10780     TopAbs_State aState = theClassifier.State();
10781     return (aState == TopAbs_IN || aState == TopAbs_ON );
10782   }
10783
10784   //================================================================================
10785   /*!
10786    * \brief Classifier of the 3D point on the TopoDS_Face
10787    *        with interaface suitable for isInside()
10788    */
10789   //================================================================================
10790
10791   struct _FaceClassifier
10792   {
10793     Extrema_ExtPS       _extremum;
10794     BRepAdaptor_Surface _surface;
10795     TopAbs_State        _state;
10796
10797     _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10798     {
10799       _extremum.Initialize( _surface,
10800                             _surface.FirstUParameter(), _surface.LastUParameter(),
10801                             _surface.FirstVParameter(), _surface.LastVParameter(),
10802                             _surface.Tolerance(), _surface.Tolerance() );
10803     }
10804     void Perform(const gp_Pnt& aPnt, double theTol)
10805     {
10806       theTol *= theTol;
10807       _state = TopAbs_OUT;
10808       _extremum.Perform(aPnt);
10809       if ( _extremum.IsDone() )
10810         for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10811           _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10812     }
10813     TopAbs_State State() const
10814     {
10815       return _state;
10816     }
10817   };
10818 }
10819
10820 //================================================================================
10821 /*!
10822   \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
10823   This method is the first step of DoubleNodeElemGroupsInRegion.
10824   \param theElems - list of groups of elements (edges or faces) to be replicated
10825   \param theNodesNot - list of groups of nodes not to replicated
10826   \param theShape - shape to detect affected elements (element which geometric center
10827          located on or inside shape). If the shape is null, detection is done on faces orientations
10828          (select elements with a gravity center on the side given by faces normals).
10829          This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
10830          The replicated nodes should be associated to affected elements.
10831   \return groups of affected elements
10832   \sa DoubleNodeElemGroupsInRegion()
10833  */
10834 //================================================================================
10835
10836 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
10837                                                    const TIDSortedElemSet& theNodesNot,
10838                                                    const TopoDS_Shape&     theShape,
10839                                                    TIDSortedElemSet&       theAffectedElems)
10840 {
10841   if ( theShape.IsNull() )
10842   {
10843     std::set<const SMDS_MeshNode*> alreadyCheckedNodes;
10844     std::set<const SMDS_MeshElement*> alreadyCheckedElems;
10845     std::set<const SMDS_MeshElement*> edgesToCheck;
10846     alreadyCheckedNodes.clear();
10847     alreadyCheckedElems.clear();
10848     edgesToCheck.clear();
10849
10850     // --- iterates on elements to be replicated and get elements by back references from their nodes
10851
10852     TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10853     for ( ;  elemItr != theElems.end(); ++elemItr )
10854     {
10855       SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10856       if (!anElem || (anElem->GetType() != SMDSAbs_Face))
10857         continue;
10858       gp_XYZ normal;
10859       SMESH_MeshAlgos::FaceNormal( anElem, normal, /*normalized=*/true );
10860       std::set<const SMDS_MeshNode*> nodesElem;
10861       nodesElem.clear();
10862       SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10863       while ( nodeItr->more() )
10864       {
10865         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10866         nodesElem.insert(aNode);
10867       }
10868       std::set<const SMDS_MeshNode*>::iterator nodit = nodesElem.begin();
10869       for (; nodit != nodesElem.end(); nodit++)
10870       {
10871         const SMDS_MeshNode* aNode = *nodit;
10872         if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10873           continue;
10874         if (alreadyCheckedNodes.find(aNode) != alreadyCheckedNodes.end())
10875           continue;
10876         alreadyCheckedNodes.insert(aNode);
10877         SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10878         while ( backElemItr->more() )
10879         {
10880           const SMDS_MeshElement* curElem = backElemItr->next();
10881           if (alreadyCheckedElems.find(curElem) != alreadyCheckedElems.end())
10882             continue;
10883           if (theElems.find(curElem) != theElems.end())
10884             continue;
10885           alreadyCheckedElems.insert(curElem);
10886           double x=0, y=0, z=0;
10887           int nb = 0;
10888           SMDS_ElemIteratorPtr nodeItr2 = curElem->nodesIterator();
10889           while ( nodeItr2->more() )
10890           {
10891             const SMDS_MeshNode* anotherNode = cast2Node(nodeItr2->next());
10892             x += anotherNode->X();
10893             y += anotherNode->Y();
10894             z += anotherNode->Z();
10895             nb++;
10896           }
10897           gp_XYZ p;
10898           p.SetCoord( x/nb -aNode->X(),
10899                       y/nb -aNode->Y(),
10900                       z/nb -aNode->Z() );
10901           if (normal*p > 0)
10902           {
10903             theAffectedElems.insert( curElem );
10904           }
10905           else if (curElem->GetType() == SMDSAbs_Edge)
10906             edgesToCheck.insert(curElem);
10907         }
10908       }
10909     }
10910     // --- add also edges lying on the set of faces (all nodes in alreadyCheckedNodes)
10911     std::set<const SMDS_MeshElement*>::iterator eit = edgesToCheck.begin();
10912     for( ; eit != edgesToCheck.end(); eit++)
10913     {
10914       bool onside = true;
10915       const SMDS_MeshElement* anEdge = *eit;
10916       SMDS_ElemIteratorPtr nodeItr = anEdge->nodesIterator();
10917       while ( nodeItr->more() )
10918       {
10919         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10920         if (alreadyCheckedNodes.find(aNode) == alreadyCheckedNodes.end())
10921         {
10922           onside = false;
10923           break;
10924         }
10925       }
10926       if (onside)
10927       {
10928         theAffectedElems.insert(anEdge);
10929       }
10930     }
10931   }
10932   else
10933   {
10934     const double aTol = Precision::Confusion();
10935     auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
10936     auto_ptr<_FaceClassifier>              aFaceClassifier;
10937     if ( theShape.ShapeType() == TopAbs_SOLID )
10938     {
10939       bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10940       bsc3d->PerformInfinitePoint(aTol);
10941     }
10942     else if (theShape.ShapeType() == TopAbs_FACE )
10943     {
10944       aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10945     }
10946
10947     // iterates on indicated elements and get elements by back references from their nodes
10948     TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10949     for ( ;  elemItr != theElems.end(); ++elemItr )
10950     {
10951       SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10952       if (!anElem)
10953         continue;
10954       SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10955       while ( nodeItr->more() )
10956       {
10957         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10958         if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10959           continue;
10960         SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10961         while ( backElemItr->more() )
10962         {
10963           const SMDS_MeshElement* curElem = backElemItr->next();
10964           if ( curElem && theElems.find(curElem) == theElems.end() &&
10965               ( bsc3d.get() ?
10966                 isInside( curElem, *bsc3d, aTol ) :
10967                 isInside( curElem, *aFaceClassifier, aTol )))
10968             theAffectedElems.insert( curElem );
10969         }
10970       }
10971     }
10972   }
10973   return true;
10974 }
10975
10976 //================================================================================
10977 /*!
10978   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10979   \param theElems - group of of elements (edges or faces) to be replicated
10980   \param theNodesNot - group of nodes not to replicate
10981   \param theShape - shape to detect affected elements (element which geometric center
10982   located on or inside shape).
10983   The replicated nodes should be associated to affected elements.
10984   \return TRUE if operation has been completed successfully, FALSE otherwise
10985 */
10986 //================================================================================
10987
10988 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
10989                                             const TIDSortedElemSet& theNodesNot,
10990                                             const TopoDS_Shape&     theShape )
10991 {
10992   if ( theShape.IsNull() )
10993     return false;
10994
10995   const double aTol = Precision::Confusion();
10996   SMESHUtils::Deleter< BRepClass3d_SolidClassifier> bsc3d;
10997   SMESHUtils::Deleter<_FaceClassifier>              aFaceClassifier;
10998   if ( theShape.ShapeType() == TopAbs_SOLID )
10999   {
11000     bsc3d._obj = new BRepClass3d_SolidClassifier( theShape );
11001     bsc3d->PerformInfinitePoint(aTol);
11002   }
11003   else if (theShape.ShapeType() == TopAbs_FACE )
11004   {
11005     aFaceClassifier._obj = new _FaceClassifier( TopoDS::Face( theShape ));
11006   }
11007
11008   // iterates on indicated elements and get elements by back references from their nodes
11009   TIDSortedElemSet anAffected;
11010   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11011   for ( ;  elemItr != theElems.end(); ++elemItr )
11012   {
11013     SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11014     if (!anElem)
11015       continue;
11016
11017     SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11018     while ( nodeItr->more() )
11019     {
11020       const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11021       if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11022         continue;
11023       SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11024       while ( backElemItr->more() )
11025       {
11026         const SMDS_MeshElement* curElem = backElemItr->next();
11027         if ( curElem && theElems.find(curElem) == theElems.end() &&
11028              ( bsc3d ?
11029                isInside( curElem, *bsc3d, aTol ) :
11030                isInside( curElem, *aFaceClassifier, aTol )))
11031           anAffected.insert( curElem );
11032       }
11033     }
11034   }
11035   return DoubleNodes( theElems, theNodesNot, anAffected );
11036 }
11037
11038 /*!
11039  *  \brief compute an oriented angle between two planes defined by four points.
11040  *  The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
11041  *  @param p0 base of the rotation axe
11042  *  @param p1 extremity of the rotation axe
11043  *  @param g1 belongs to the first plane
11044  *  @param g2 belongs to the second plane
11045  */
11046 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
11047 {
11048   gp_Vec vref(p0, p1);
11049   gp_Vec v1(p0, g1);
11050   gp_Vec v2(p0, g2);
11051   gp_Vec n1 = vref.Crossed(v1);
11052   gp_Vec n2 = vref.Crossed(v2);
11053   try {
11054     return n2.AngleWithRef(n1, vref);
11055   }
11056   catch ( Standard_Failure ) {
11057   }
11058   return Max( v1.Magnitude(), v2.Magnitude() );
11059 }
11060
11061 /*!
11062  * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11063  *  The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
11064  * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
11065  * Triangles are transformed into prisms, and quadrangles into hexahedrons.
11066  * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
11067  * 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.
11068  * 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.
11069  * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
11070  * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
11071  * \param theElems - list of groups of volumes, where a group of volume is a set of
11072  *        SMDS_MeshElements sorted by Id.
11073  * \param createJointElems - if TRUE, create the elements
11074  * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
11075  *        the boundary between \a theDomains and the rest mesh
11076  * \return TRUE if operation has been completed successfully, FALSE otherwise
11077  */
11078 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11079                                                      bool                                 createJointElems,
11080                                                      bool                                 onAllBoundaries)
11081 {
11082   MESSAGE("----------------------------------------------");
11083   MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11084   MESSAGE("----------------------------------------------");
11085
11086   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11087   meshDS->BuildDownWardConnectivity(true);
11088   CHRONO(50);
11089   SMDS_UnstructuredGrid *grid = meshDS->getGrid();
11090
11091   // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11092   //     build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11093   //     build the list of nodes shared by 2 or more domains, with their domain indexes
11094
11095   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11096   std::map<int,int>celldom; // cell vtkId --> domain
11097   std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains;  // oldNode --> (id domain --> id cell)
11098   std::map<int, std::map<int,int> > nodeDomains; // oldId -->  (domainId --> newId)
11099   faceDomains.clear();
11100   celldom.clear();
11101   cellDomains.clear();
11102   nodeDomains.clear();
11103   std::map<int,int> emptyMap;
11104   std::set<int> emptySet;
11105   emptyMap.clear();
11106
11107   MESSAGE(".. Number of domains :"<<theElems.size());
11108
11109   TIDSortedElemSet theRestDomElems;
11110   const int iRestDom  = -1;
11111   const int idom0     = onAllBoundaries ? iRestDom : 0;
11112   const int nbDomains = theElems.size();
11113
11114   // Check if the domains do not share an element
11115   for (int idom = 0; idom < nbDomains-1; idom++)
11116   {
11117     //       MESSAGE("... Check of domain #" << idom);
11118     const TIDSortedElemSet& domain = theElems[idom];
11119     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11120     for (; elemItr != domain.end(); ++elemItr)
11121     {
11122       const SMDS_MeshElement* anElem = *elemItr;
11123       int idombisdeb = idom + 1 ;
11124       // check if the element belongs to a domain further in the list
11125       for ( size_t idombis = idombisdeb; idombis < theElems.size(); idombis++ )
11126       {
11127         const TIDSortedElemSet& domainbis = theElems[idombis];
11128         if ( domainbis.count( anElem ))
11129         {
11130           MESSAGE(".... Domain #" << idom);
11131           MESSAGE(".... Domain #" << idombis);
11132           throw SALOME_Exception("The domains are not disjoint.");
11133           return false ;
11134         }
11135       }
11136     }
11137   }
11138
11139   for (int idom = 0; idom < nbDomains; idom++)
11140   {
11141
11142     // --- build a map (face to duplicate --> volume to modify)
11143     //     with all the faces shared by 2 domains (group of elements)
11144     //     and corresponding volume of this domain, for each shared face.
11145     //     a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11146
11147     MESSAGE("... Neighbors of domain #" << idom);
11148     const TIDSortedElemSet& domain = theElems[idom];
11149     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11150     for (; elemItr != domain.end(); ++elemItr)
11151     {
11152       const SMDS_MeshElement* anElem = *elemItr;
11153       if (!anElem)
11154         continue;
11155       int vtkId = anElem->getVtkId();
11156       //MESSAGE("  vtkId " << vtkId << " smdsId " << anElem->GetID());
11157       int neighborsVtkIds[NBMAXNEIGHBORS];
11158       int downIds[NBMAXNEIGHBORS];
11159       unsigned char downTypes[NBMAXNEIGHBORS];
11160       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11161       for (int n = 0; n < nbNeighbors; n++)
11162       {
11163         int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
11164         const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11165         if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
11166         {
11167           bool ok = false;
11168           for ( size_t idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
11169           {
11170             // MESSAGE("Domain " << idombis);
11171             const TIDSortedElemSet& domainbis = theElems[idombis];
11172             if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11173           }
11174           if ( ok || onAllBoundaries ) // the characteristics of the face is stored
11175           {
11176             DownIdType face(downIds[n], downTypes[n]);
11177             if (!faceDomains[face].count(idom))
11178             {
11179               faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11180               celldom[vtkId] = idom;
11181               //MESSAGE("       cell with a border " << vtkId << " domain " << idom);
11182             }
11183             if ( !ok )
11184             {
11185               theRestDomElems.insert( elem );
11186               faceDomains[face][iRestDom] = neighborsVtkIds[n];
11187               celldom[neighborsVtkIds[n]] = iRestDom;
11188             }
11189           }
11190         }
11191       }
11192     }
11193   }
11194
11195   //MESSAGE("Number of shared faces " << faceDomains.size());
11196   std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11197
11198   // --- explore the shared faces domain by domain,
11199   //     explore the nodes of the face and see if they belong to a cell in the domain,
11200   //     which has only a node or an edge on the border (not a shared face)
11201
11202   for (int idomain = idom0; idomain < nbDomains; idomain++)
11203   {
11204     //MESSAGE("Domain " << idomain);
11205     const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
11206     itface = faceDomains.begin();
11207     for (; itface != faceDomains.end(); ++itface)
11208     {
11209       const std::map<int, int>& domvol = itface->second;
11210       if (!domvol.count(idomain))
11211         continue;
11212       DownIdType face = itface->first;
11213       //MESSAGE(" --- face " << face.cellId);
11214       std::set<int> oldNodes;
11215       oldNodes.clear();
11216       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11217       std::set<int>::iterator itn = oldNodes.begin();
11218       for (; itn != oldNodes.end(); ++itn)
11219       {
11220         int oldId = *itn;
11221         //MESSAGE("     node " << oldId);
11222         vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11223         for (int i=0; i<l.ncells; i++)
11224         {
11225           int vtkId = l.cells[i];
11226           const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
11227           if (!domain.count(anElem))
11228             continue;
11229           int vtkType = grid->GetCellType(vtkId);
11230           int downId = grid->CellIdToDownId(vtkId);
11231           if (downId < 0)
11232           {
11233             MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11234             continue; // not OK at this stage of the algorithm:
11235             //no cells created after BuildDownWardConnectivity
11236           }
11237           DownIdType aCell(downId, vtkType);
11238           cellDomains[aCell][idomain] = vtkId;
11239           celldom[vtkId] = idomain;
11240           //MESSAGE("       cell " << vtkId << " domain " << idomain);
11241         }
11242       }
11243     }
11244   }
11245
11246   // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11247   //     for each shared face, get the nodes
11248   //     for each node, for each domain of the face, create a clone of the node
11249
11250   // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11251   //     junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11252   //     the value is the ordered domain ids. (more than 4 domains not taken into account)
11253
11254   std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11255   std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11256   std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11257
11258   MESSAGE(".. Duplication of the nodes");
11259   for (int idomain = idom0; idomain < nbDomains; idomain++)
11260   {
11261     itface = faceDomains.begin();
11262     for (; itface != faceDomains.end(); ++itface)
11263     {
11264       const std::map<int, int>& domvol = itface->second;
11265       if (!domvol.count(idomain))
11266         continue;
11267       DownIdType face = itface->first;
11268       //MESSAGE(" --- face " << face.cellId);
11269       std::set<int> oldNodes;
11270       oldNodes.clear();
11271       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11272       std::set<int>::iterator itn = oldNodes.begin();
11273       for (; itn != oldNodes.end(); ++itn)
11274       {
11275         int oldId = *itn;
11276         if (nodeDomains[oldId].empty())
11277         {
11278           nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11279           //MESSAGE("-+-+-b     oldNode " << oldId << " domain " << idomain);
11280         }
11281         std::map<int, int>::const_iterator itdom = domvol.begin();
11282         for (; itdom != domvol.end(); ++itdom)
11283         {
11284           int idom = itdom->first;
11285           //MESSAGE("         domain " << idom);
11286           if (!nodeDomains[oldId].count(idom)) // --- node to clone
11287           {
11288             if (nodeDomains[oldId].size() >= 2) // a multiple node
11289             {
11290               vector<int> orderedDoms;
11291               //MESSAGE("multiple node " << oldId);
11292               if (mutipleNodes.count(oldId))
11293                 orderedDoms = mutipleNodes[oldId];
11294               else
11295               {
11296                 map<int,int>::iterator it = nodeDomains[oldId].begin();
11297                 for (; it != nodeDomains[oldId].end(); ++it)
11298                   orderedDoms.push_back(it->first);
11299               }
11300               orderedDoms.push_back(idom); // TODO order ==> push_front or back
11301               //stringstream txt;
11302               //for (int i=0; i<orderedDoms.size(); i++)
11303               //  txt << orderedDoms[i] << " ";
11304               //MESSAGE("orderedDoms " << txt.str());
11305               mutipleNodes[oldId] = orderedDoms;
11306             }
11307             double *coords = grid->GetPoint(oldId);
11308             SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11309             copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11310             int newId = newNode->getVtkId();
11311             nodeDomains[oldId][idom] = newId; // cloned node for other domains
11312             //MESSAGE("-+-+-c     oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11313           }
11314         }
11315       }
11316     }
11317   }
11318
11319   MESSAGE(".. Creation of elements");
11320   for (int idomain = idom0; idomain < nbDomains; idomain++)
11321   {
11322     itface = faceDomains.begin();
11323     for (; itface != faceDomains.end(); ++itface)
11324     {
11325       std::map<int, int> domvol = itface->second;
11326       if (!domvol.count(idomain))
11327         continue;
11328       DownIdType face = itface->first;
11329       //MESSAGE(" --- face " << face.cellId);
11330       std::set<int> oldNodes;
11331       oldNodes.clear();
11332       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11333       int nbMultipleNodes = 0;
11334       std::set<int>::iterator itn = oldNodes.begin();
11335       for (; itn != oldNodes.end(); ++itn)
11336       {
11337         int oldId = *itn;
11338         if (mutipleNodes.count(oldId))
11339           nbMultipleNodes++;
11340       }
11341       if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11342       {
11343         //MESSAGE("multiple Nodes detected on a shared face");
11344         int downId = itface->first.cellId;
11345         unsigned char cellType = itface->first.cellType;
11346         // --- shared edge or shared face ?
11347         if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11348         {
11349           int nodes[3];
11350           int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11351           for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11352             if (mutipleNodes.count(nodes[i]))
11353               if (!mutipleNodesToFace.count(nodes[i]))
11354                 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11355         }
11356         else // shared face (between two volumes)
11357         {
11358           int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11359           const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11360           const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11361           for (int ie =0; ie < nbEdges; ie++)
11362           {
11363             int nodes[3];
11364             int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11365             if ( mutipleNodes.count(nodes[0]) && mutipleNodes.count( nodes[ nbNodes-1 ]))
11366             {
11367               vector<int> vn0 = mutipleNodes[nodes[0]];
11368               vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11369               vector<int> doms;
11370               for ( size_t i0 = 0; i0 < vn0.size(); i0++ )
11371                 for ( size_t i1 = 0; i1 < vn1.size(); i1++ )
11372                   if ( vn0[i0] == vn1[i1] )
11373                     doms.push_back( vn0[ i0 ]);
11374               if ( doms.size() > 2 )
11375               {
11376                 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11377                 double *coords = grid->GetPoint(nodes[0]);
11378                 gp_Pnt p0(coords[0], coords[1], coords[2]);
11379                 coords = grid->GetPoint(nodes[nbNodes - 1]);
11380                 gp_Pnt p1(coords[0], coords[1], coords[2]);
11381                 gp_Pnt gref;
11382                 int vtkVolIds[1000];  // an edge can belong to a lot of volumes
11383                 map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
11384                 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11385                 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11386                 for ( size_t id = 0; id < doms.size(); id++ )
11387                 {
11388                   int idom = doms[id];
11389                   const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11390                   for ( int ivol = 0; ivol < nbvol; ivol++ )
11391                   {
11392                     int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
11393                     SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
11394                     if (domain.count(elem))
11395                     {
11396                       SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
11397                       domvol[idom] = svol;
11398                       //MESSAGE("  domain " << idom << " volume " << elem->GetID());
11399                       double values[3];
11400                       vtkIdType npts = 0;
11401                       vtkIdType* pts = 0;
11402                       grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11403                       SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
11404                       if (id ==0)
11405                       {
11406                         gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
11407                         angleDom[idom] = 0;
11408                       }
11409                       else
11410                       {
11411                         gp_Pnt g(values[0], values[1], values[2]);
11412                         angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11413                         //MESSAGE("  angle=" << angleDom[idom]);
11414                       }
11415                       break;
11416                     }
11417                   }
11418                 }
11419                 map<double, int> sortedDom; // sort domains by angle
11420                 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11421                   sortedDom[ia->second] = ia->first;
11422                 vector<int> vnodes;
11423                 vector<int> vdom;
11424                 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11425                 {
11426                   vdom.push_back(ib->second);
11427                   //MESSAGE("  ordered domain " << ib->second << "  angle " << ib->first);
11428                 }
11429                 for (int ino = 0; ino < nbNodes; ino++)
11430                   vnodes.push_back(nodes[ino]);
11431                 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11432               }
11433             }
11434           }
11435         }
11436       }
11437     }
11438   }
11439
11440   // --- iterate on shared faces (volumes to modify, face to extrude)
11441   //     get node id's of the face (id SMDS = id VTK)
11442   //     create flat element with old and new nodes if requested
11443
11444   // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11445   //     (domain1 X domain2) = domain1 + MAXINT*domain2
11446
11447   std::map<int, std::map<long,int> > nodeQuadDomains;
11448   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11449
11450   MESSAGE(".. Creation of elements: simple junction");
11451   if (createJointElems)
11452   {
11453     int idg;
11454     string joints2DName = "joints2D";
11455     mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
11456     SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11457     string joints3DName = "joints3D";
11458     mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
11459     SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11460
11461     itface = faceDomains.begin();
11462     for (; itface != faceDomains.end(); ++itface)
11463     {
11464       DownIdType face = itface->first;
11465       std::set<int> oldNodes;
11466       std::set<int>::iterator itn;
11467       oldNodes.clear();
11468       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11469
11470       std::map<int, int> domvol = itface->second;
11471       std::map<int, int>::iterator itdom = domvol.begin();
11472       int dom1 = itdom->first;
11473       int vtkVolId = itdom->second;
11474       itdom++;
11475       int dom2 = itdom->first;
11476       SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11477                                                        nodeQuadDomains);
11478       stringstream grpname;
11479       grpname << "j_";
11480       if (dom1 < dom2)
11481         grpname << dom1 << "_" << dom2;
11482       else
11483         grpname << dom2 << "_" << dom1;
11484       string namegrp = grpname.str();
11485       if (!mapOfJunctionGroups.count(namegrp))
11486         mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
11487       SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11488       if (sgrp)
11489         sgrp->Add(vol->GetID());
11490       if (vol->GetType() == SMDSAbs_Volume)
11491         joints3DGrp->Add(vol->GetID());
11492       else if (vol->GetType() == SMDSAbs_Face)
11493         joints2DGrp->Add(vol->GetID());
11494     }
11495   }
11496
11497   // --- create volumes on multiple domain intersection if requested
11498   //     iterate on mutipleNodesToFace
11499   //     iterate on edgesMultiDomains
11500
11501   MESSAGE(".. Creation of elements: multiple junction");
11502   if (createJointElems)
11503   {
11504     // --- iterate on mutipleNodesToFace
11505
11506     std::map<int, std::vector<int> >::iterator itn =  mutipleNodesToFace.begin();
11507     for (; itn != mutipleNodesToFace.end(); ++itn)
11508     {
11509       int node = itn->first;
11510       vector<int> orderDom = itn->second;
11511       vector<vtkIdType> orderedNodes;
11512       for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11513         orderedNodes.push_back( nodeDomains[ node ][ orderDom[ idom ]]);
11514       SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11515
11516       stringstream grpname;
11517       grpname << "m2j_";
11518       grpname << 0 << "_" << 0;
11519       int idg;
11520       string namegrp = grpname.str();
11521       if (!mapOfJunctionGroups.count(namegrp))
11522         mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
11523       SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11524       if (sgrp)
11525         sgrp->Add(face->GetID());
11526     }
11527
11528     // --- iterate on edgesMultiDomains
11529
11530     std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11531     for (; ite != edgesMultiDomains.end(); ++ite)
11532     {
11533       vector<int> nodes = ite->first;
11534       vector<int> orderDom = ite->second;
11535       vector<vtkIdType> orderedNodes;
11536       if (nodes.size() == 2)
11537       {
11538         //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11539         for ( size_t ino = 0; ino < nodes.size(); ino++ )
11540           if ( orderDom.size() == 3 )
11541             for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11542               orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11543           else
11544             for (int idom = orderDom.size()-1; idom >=0; idom--)
11545               orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11546         SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11547
11548         int idg;
11549         string namegrp = "jointsMultiples";
11550         if (!mapOfJunctionGroups.count(namegrp))
11551           mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11552         SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11553         if (sgrp)
11554           sgrp->Add(vol->GetID());
11555       }
11556       else
11557       {
11558         //INFOS("Quadratic multiple joints not implemented");
11559         // TODO quadratic nodes
11560       }
11561     }
11562   }
11563
11564   // --- list the explicit faces and edges of the mesh that need to be modified,
11565   //     i.e. faces and edges built with one or more duplicated nodes.
11566   //     associate these faces or edges to their corresponding domain.
11567   //     only the first domain found is kept when a face or edge is shared
11568
11569   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11570   std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11571   faceOrEdgeDom.clear();
11572   feDom.clear();
11573
11574   MESSAGE(".. Modification of elements");
11575   for (int idomain = idom0; idomain < nbDomains; idomain++)
11576   {
11577     std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11578     for (; itnod != nodeDomains.end(); ++itnod)
11579     {
11580       int oldId = itnod->first;
11581       //MESSAGE("     node " << oldId);
11582       vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11583       for (int i = 0; i < l.ncells; i++)
11584       {
11585         int vtkId = l.cells[i];
11586         int vtkType = grid->GetCellType(vtkId);
11587         int downId = grid->CellIdToDownId(vtkId);
11588         if (downId < 0)
11589           continue; // new cells: not to be modified
11590         DownIdType aCell(downId, vtkType);
11591         int volParents[1000];
11592         int nbvol = grid->GetParentVolumes(volParents, vtkId);
11593         for (int j = 0; j < nbvol; j++)
11594           if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11595             if (!feDom.count(vtkId))
11596             {
11597               feDom[vtkId] = idomain;
11598               faceOrEdgeDom[aCell] = emptyMap;
11599               faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11600               //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
11601               //        << " type " << vtkType << " downId " << downId);
11602             }
11603       }
11604     }
11605   }
11606
11607   // --- iterate on shared faces (volumes to modify, face to extrude)
11608   //     get node id's of the face
11609   //     replace old nodes by new nodes in volumes, and update inverse connectivity
11610
11611   std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11612   for (int m=0; m<3; m++)
11613   {
11614     std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11615     itface = (*amap).begin();
11616     for (; itface != (*amap).end(); ++itface)
11617     {
11618       DownIdType face = itface->first;
11619       std::set<int> oldNodes;
11620       std::set<int>::iterator itn;
11621       oldNodes.clear();
11622       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11623       //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11624       std::map<int, int> localClonedNodeIds;
11625
11626       std::map<int, int> domvol = itface->second;
11627       std::map<int, int>::iterator itdom = domvol.begin();
11628       for (; itdom != domvol.end(); ++itdom)
11629       {
11630         int idom = itdom->first;
11631         int vtkVolId = itdom->second;
11632         //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
11633         localClonedNodeIds.clear();
11634         for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11635         {
11636           int oldId = *itn;
11637           if (nodeDomains[oldId].count(idom))
11638           {
11639             localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11640             //MESSAGE("     node " << oldId << " --> " << localClonedNodeIds[oldId]);
11641           }
11642         }
11643         meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11644       }
11645     }
11646   }
11647
11648   // Remove empty groups (issue 0022812)
11649   std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
11650   for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
11651   {
11652     if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
11653       myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
11654   }
11655
11656   meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11657   grid->BuildLinks();
11658
11659   CHRONOSTOP(50);
11660   counters::stats();
11661   return true;
11662 }
11663
11664 /*!
11665  * \brief Double nodes on some external faces and create flat elements.
11666  * Flat elements are mainly used by some types of mechanic calculations.
11667  *
11668  * Each group of the list must be constituted of faces.
11669  * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11670  * @param theElems - list of groups of faces, where a group of faces is a set of
11671  * SMDS_MeshElements sorted by Id.
11672  * @return TRUE if operation has been completed successfully, FALSE otherwise
11673  */
11674 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11675 {
11676   MESSAGE("-------------------------------------------------");
11677   MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11678   MESSAGE("-------------------------------------------------");
11679
11680   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11681
11682   // --- For each group of faces
11683   //     duplicate the nodes, create a flat element based on the face
11684   //     replace the nodes of the faces by their clones
11685
11686   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11687   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11688   clonedNodes.clear();
11689   intermediateNodes.clear();
11690   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11691   mapOfJunctionGroups.clear();
11692
11693   for ( size_t idom = 0; idom < theElems.size(); idom++ )
11694   {
11695     const TIDSortedElemSet&           domain = theElems[idom];
11696     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11697     for ( ; elemItr != domain.end(); ++elemItr )
11698     {
11699       SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11700       SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
11701       if (!aFace)
11702         continue;
11703       // MESSAGE("aFace=" << aFace->GetID());
11704       bool isQuad = aFace->IsQuadratic();
11705       vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11706
11707       // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11708
11709       SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
11710       while (nodeIt->more())
11711       {
11712         const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
11713         bool isMedium = isQuad && (aFace->IsMediumNode(node));
11714         if (isMedium)
11715           ln2.push_back(node);
11716         else
11717           ln0.push_back(node);
11718
11719         const SMDS_MeshNode* clone = 0;
11720         if (!clonedNodes.count(node))
11721         {
11722           clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11723           copyPosition( node, clone );
11724           clonedNodes[node] = clone;
11725         }
11726         else
11727           clone = clonedNodes[node];
11728
11729         if (isMedium)
11730           ln3.push_back(clone);
11731         else
11732           ln1.push_back(clone);
11733
11734         const SMDS_MeshNode* inter = 0;
11735         if (isQuad && (!isMedium))
11736         {
11737           if (!intermediateNodes.count(node))
11738           {
11739             inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11740             copyPosition( node, inter );
11741             intermediateNodes[node] = inter;
11742           }
11743           else
11744             inter = intermediateNodes[node];
11745           ln4.push_back(inter);
11746         }
11747       }
11748
11749       // --- extrude the face
11750
11751       vector<const SMDS_MeshNode*> ln;
11752       SMDS_MeshVolume* vol = 0;
11753       vtkIdType aType = aFace->GetVtkType();
11754       switch (aType)
11755       {
11756       case VTK_TRIANGLE:
11757         vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11758         // MESSAGE("vol prism " << vol->GetID());
11759         ln.push_back(ln1[0]);
11760         ln.push_back(ln1[1]);
11761         ln.push_back(ln1[2]);
11762         break;
11763       case VTK_QUAD:
11764         vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11765         // MESSAGE("vol hexa " << vol->GetID());
11766         ln.push_back(ln1[0]);
11767         ln.push_back(ln1[1]);
11768         ln.push_back(ln1[2]);
11769         ln.push_back(ln1[3]);
11770         break;
11771       case VTK_QUADRATIC_TRIANGLE:
11772         vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11773                                 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11774         // MESSAGE("vol quad prism " << vol->GetID());
11775         ln.push_back(ln1[0]);
11776         ln.push_back(ln1[1]);
11777         ln.push_back(ln1[2]);
11778         ln.push_back(ln3[0]);
11779         ln.push_back(ln3[1]);
11780         ln.push_back(ln3[2]);
11781         break;
11782       case VTK_QUADRATIC_QUAD:
11783         //              vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11784         //                                      ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11785         //                                      ln4[0], ln4[1], ln4[2], ln4[3]);
11786         vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11787                                 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11788                                 ln4[0], ln4[1], ln4[2], ln4[3]);
11789         // MESSAGE("vol quad hexa " << vol->GetID());
11790         ln.push_back(ln1[0]);
11791         ln.push_back(ln1[1]);
11792         ln.push_back(ln1[2]);
11793         ln.push_back(ln1[3]);
11794         ln.push_back(ln3[0]);
11795         ln.push_back(ln3[1]);
11796         ln.push_back(ln3[2]);
11797         ln.push_back(ln3[3]);
11798         break;
11799       case VTK_POLYGON:
11800         break;
11801       default:
11802         break;
11803       }
11804
11805       if (vol)
11806       {
11807         stringstream grpname;
11808         grpname << "jf_";
11809         grpname << idom;
11810         int idg;
11811         string namegrp = grpname.str();
11812         if (!mapOfJunctionGroups.count(namegrp))
11813           mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11814         SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11815         if (sgrp)
11816           sgrp->Add(vol->GetID());
11817       }
11818
11819       // --- modify the face
11820
11821       aFace->ChangeNodes(&ln[0], ln.size());
11822     }
11823   }
11824   return true;
11825 }
11826
11827 /*!
11828  *  \brief identify all the elements around a geom shape, get the faces delimiting the hole
11829  *  Build groups of volume to remove, groups of faces to replace on the skin of the object,
11830  *  groups of faces to remove inside the object, (idem edges).
11831  *  Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11832  */
11833 void SMESH_MeshEditor::CreateHoleSkin(double                          radius,
11834                                       const TopoDS_Shape&             theShape,
11835                                       SMESH_NodeSearcher*             theNodeSearcher,
11836                                       const char*                     groupName,
11837                                       std::vector<double>&            nodesCoords,
11838                                       std::vector<std::vector<int> >& listOfListOfNodes)
11839 {
11840   MESSAGE("--------------------------------");
11841   MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11842   MESSAGE("--------------------------------");
11843
11844   // --- zone of volumes to remove is given :
11845   //     1 either by a geom shape (one or more vertices) and a radius,
11846   //     2 either by a group of nodes (representative of the shape)to use with the radius,
11847   //     3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11848   //     In the case 2, the group of nodes is an external group of nodes from another mesh,
11849   //     In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11850   //     defined by it's name.
11851
11852   SMESHDS_GroupBase* groupDS = 0;
11853   SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11854   while ( groupIt->more() )
11855   {
11856     groupDS = 0;
11857     SMESH_Group * group = groupIt->next();
11858     if ( !group ) continue;
11859     groupDS = group->GetGroupDS();
11860     if ( !groupDS || groupDS->IsEmpty() ) continue;
11861     std::string grpName = group->GetName();
11862     //MESSAGE("grpName=" << grpName);
11863     if (grpName == groupName)
11864       break;
11865     else
11866       groupDS = 0;
11867   }
11868
11869   bool isNodeGroup = false;
11870   bool isNodeCoords = false;
11871   if (groupDS)
11872   {
11873     if (groupDS->GetType() != SMDSAbs_Node)
11874       return;
11875     isNodeGroup = true;     // a group of nodes exists and it is in this mesh
11876   }
11877
11878   if (nodesCoords.size() > 0)
11879     isNodeCoords = true; // a list o nodes given by their coordinates
11880   //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11881
11882   // --- define groups to build
11883
11884   int idg; // --- group of SMDS volumes
11885   string grpvName = groupName;
11886   grpvName += "_vol";
11887   SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
11888   if (!grp)
11889   {
11890     MESSAGE("group not created " << grpvName);
11891     return;
11892   }
11893   SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
11894
11895   int idgs; // --- group of SMDS faces on the skin
11896   string grpsName = groupName;
11897   grpsName += "_skin";
11898   SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
11899   if (!grps)
11900   {
11901     MESSAGE("group not created " << grpsName);
11902     return;
11903   }
11904   SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
11905
11906   int idgi; // --- group of SMDS faces internal (several shapes)
11907   string grpiName = groupName;
11908   grpiName += "_internalFaces";
11909   SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
11910   if (!grpi)
11911   {
11912     MESSAGE("group not created " << grpiName);
11913     return;
11914   }
11915   SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
11916
11917   int idgei; // --- group of SMDS faces internal (several shapes)
11918   string grpeiName = groupName;
11919   grpeiName += "_internalEdges";
11920   SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
11921   if (!grpei)
11922   {
11923     MESSAGE("group not created " << grpeiName);
11924     return;
11925   }
11926   SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
11927
11928   // --- build downward connectivity
11929
11930   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11931   meshDS->BuildDownWardConnectivity(true);
11932   SMDS_UnstructuredGrid* grid = meshDS->getGrid();
11933
11934   // --- set of volumes detected inside
11935
11936   std::set<int> setOfInsideVol;
11937   std::set<int> setOfVolToCheck;
11938
11939   std::vector<gp_Pnt> gpnts;
11940   gpnts.clear();
11941
11942   if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
11943   {
11944     MESSAGE("group of nodes provided");
11945     SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
11946     while ( elemIt->more() )
11947     {
11948       const SMDS_MeshElement* elem = elemIt->next();
11949       if (!elem)
11950         continue;
11951       const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
11952       if (!node)
11953         continue;
11954       SMDS_MeshElement* vol = 0;
11955       SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
11956       while (volItr->more())
11957       {
11958         vol = (SMDS_MeshElement*)volItr->next();
11959         setOfInsideVol.insert(vol->getVtkId());
11960         sgrp->Add(vol->GetID());
11961       }
11962     }
11963   }
11964   else if (isNodeCoords)
11965   {
11966     MESSAGE("list of nodes coordinates provided");
11967     size_t i = 0;
11968     int k = 0;
11969     while ( i < nodesCoords.size()-2 )
11970     {
11971       double x = nodesCoords[i++];
11972       double y = nodesCoords[i++];
11973       double z = nodesCoords[i++];
11974       gp_Pnt p = gp_Pnt(x, y ,z);
11975       gpnts.push_back(p);
11976       MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
11977       k++;
11978     }
11979   }
11980   else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
11981   {
11982     MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
11983     TopTools_IndexedMapOfShape vertexMap;
11984     TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
11985     gp_Pnt p = gp_Pnt(0,0,0);
11986     if (vertexMap.Extent() < 1)
11987       return;
11988
11989     for ( int i = 1; i <= vertexMap.Extent(); ++i )
11990     {
11991       const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
11992       p = BRep_Tool::Pnt(vertex);
11993       gpnts.push_back(p);
11994       MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
11995     }
11996   }
11997
11998   if (gpnts.size() > 0)
11999   {
12000     int nodeId = 0;
12001     const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
12002     if (startNode)
12003       nodeId = startNode->GetID();
12004     MESSAGE("nodeId " << nodeId);
12005
12006     double radius2 = radius*radius;
12007     MESSAGE("radius2 " << radius2);
12008
12009     // --- volumes on start node
12010
12011     setOfVolToCheck.clear();
12012     SMDS_MeshElement* startVol = 0;
12013     SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
12014     while (volItr->more())
12015     {
12016       startVol = (SMDS_MeshElement*)volItr->next();
12017       setOfVolToCheck.insert(startVol->getVtkId());
12018     }
12019     if (setOfVolToCheck.empty())
12020     {
12021       MESSAGE("No volumes found");
12022       return;
12023     }
12024
12025     // --- starting with central volumes then their neighbors, check if they are inside
12026     //     or outside the domain, until no more new neighbor volume is inside.
12027     //     Fill the group of inside volumes
12028
12029     std::map<int, double> mapOfNodeDistance2;
12030     mapOfNodeDistance2.clear();
12031     std::set<int> setOfOutsideVol;
12032     while (!setOfVolToCheck.empty())
12033     {
12034       std::set<int>::iterator it = setOfVolToCheck.begin();
12035       int vtkId = *it;
12036       MESSAGE("volume to check,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12037       bool volInside = false;
12038       vtkIdType npts = 0;
12039       vtkIdType* pts = 0;
12040       grid->GetCellPoints(vtkId, npts, pts);
12041       for (int i=0; i<npts; i++)
12042       {
12043         double distance2 = 0;
12044         if (mapOfNodeDistance2.count(pts[i]))
12045         {
12046           distance2 = mapOfNodeDistance2[pts[i]];
12047           MESSAGE("point " << pts[i] << " distance2 " << distance2);
12048         }
12049         else
12050         {
12051           double *coords = grid->GetPoint(pts[i]);
12052           gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
12053           distance2 = 1.E40;
12054           for ( size_t j = 0; j < gpnts.size(); j++ )
12055           {
12056             double d2 = aPoint.SquareDistance( gpnts[ j ]);
12057             if (d2 < distance2)
12058             {
12059               distance2 = d2;
12060               if (distance2 < radius2)
12061                 break;
12062             }
12063           }
12064           mapOfNodeDistance2[pts[i]] = distance2;
12065           MESSAGE("  point "  << pts[i]  << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " <<  coords[2]);
12066         }
12067         if (distance2 < radius2)
12068         {
12069           volInside = true; // one or more nodes inside the domain
12070           sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12071           break;
12072         }
12073       }
12074       if (volInside)
12075       {
12076         setOfInsideVol.insert(vtkId);
12077         MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12078         int neighborsVtkIds[NBMAXNEIGHBORS];
12079         int downIds[NBMAXNEIGHBORS];
12080         unsigned char downTypes[NBMAXNEIGHBORS];
12081         int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12082         for (int n = 0; n < nbNeighbors; n++)
12083           if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12084             setOfVolToCheck.insert(neighborsVtkIds[n]);
12085       }
12086       else
12087       {
12088         setOfOutsideVol.insert(vtkId);
12089         MESSAGE("  volume outside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12090       }
12091       setOfVolToCheck.erase(vtkId);
12092     }
12093   }
12094
12095   // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12096   //     If yes, add the volume to the inside set
12097
12098   bool addedInside = true;
12099   std::set<int> setOfVolToReCheck;
12100   while (addedInside)
12101   {
12102     MESSAGE(" --------------------------- re check");
12103     addedInside = false;
12104     std::set<int>::iterator itv = setOfInsideVol.begin();
12105     for (; itv != setOfInsideVol.end(); ++itv)
12106     {
12107       int vtkId = *itv;
12108       int neighborsVtkIds[NBMAXNEIGHBORS];
12109       int downIds[NBMAXNEIGHBORS];
12110       unsigned char downTypes[NBMAXNEIGHBORS];
12111       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12112       for (int n = 0; n < nbNeighbors; n++)
12113         if (!setOfInsideVol.count(neighborsVtkIds[n]))
12114           setOfVolToReCheck.insert(neighborsVtkIds[n]);
12115     }
12116     setOfVolToCheck = setOfVolToReCheck;
12117     setOfVolToReCheck.clear();
12118     while  (!setOfVolToCheck.empty())
12119     {
12120       std::set<int>::iterator it = setOfVolToCheck.begin();
12121       int vtkId = *it;
12122       if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12123       {
12124         MESSAGE("volume to recheck,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12125         int countInside = 0;
12126         int neighborsVtkIds[NBMAXNEIGHBORS];
12127         int downIds[NBMAXNEIGHBORS];
12128         unsigned char downTypes[NBMAXNEIGHBORS];
12129         int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12130         for (int n = 0; n < nbNeighbors; n++)
12131           if (setOfInsideVol.count(neighborsVtkIds[n]))
12132             countInside++;
12133         MESSAGE("countInside " << countInside);
12134         if (countInside > 1)
12135         {
12136           MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12137           setOfInsideVol.insert(vtkId);
12138           sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12139           addedInside = true;
12140         }
12141         else
12142           setOfVolToReCheck.insert(vtkId);
12143       }
12144       setOfVolToCheck.erase(vtkId);
12145     }
12146   }
12147
12148   // --- map of Downward faces at the boundary, inside the global volume
12149   //     map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12150   //     fill group of SMDS faces inside the volume (when several volume shapes)
12151   //     fill group of SMDS faces on the skin of the global volume (if skin)
12152
12153   std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12154   std::map<DownIdType, int, DownIdCompare> skinFaces;     // faces on the skin of the global volume --> corresponding cell
12155   std::set<int>::iterator it = setOfInsideVol.begin();
12156   for (; it != setOfInsideVol.end(); ++it)
12157   {
12158     int vtkId = *it;
12159     //MESSAGE("  vtkId " << vtkId  << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12160     int neighborsVtkIds[NBMAXNEIGHBORS];
12161     int downIds[NBMAXNEIGHBORS];
12162     unsigned char downTypes[NBMAXNEIGHBORS];
12163     int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12164     for (int n = 0; n < nbNeighbors; n++)
12165     {
12166       int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12167       if (neighborDim == 3)
12168       {
12169         if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12170         {
12171           DownIdType face(downIds[n], downTypes[n]);
12172           boundaryFaces[face] = vtkId;
12173         }
12174         // if the face between to volumes is in the mesh, get it (internal face between shapes)
12175         int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12176         if (vtkFaceId >= 0)
12177         {
12178           sgrpi->Add(meshDS->fromVtkToSmds(vtkFaceId));
12179           // find also the smds edges on this face
12180           int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12181           const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12182           const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12183           for (int i = 0; i < nbEdges; i++)
12184           {
12185             int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12186             if (vtkEdgeId >= 0)
12187               sgrpei->Add(meshDS->fromVtkToSmds(vtkEdgeId));
12188           }
12189         }
12190       }
12191       else if (neighborDim == 2) // skin of the volume
12192       {
12193         DownIdType face(downIds[n], downTypes[n]);
12194         skinFaces[face] = vtkId;
12195         int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12196         if (vtkFaceId >= 0)
12197           sgrps->Add(meshDS->fromVtkToSmds(vtkFaceId));
12198       }
12199     }
12200   }
12201
12202   // --- identify the edges constituting the wire of each subshape on the skin
12203   //     define polylines with the nodes of edges, equivalent to wires
12204   //     project polylines on subshapes, and partition, to get geom faces
12205
12206   std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12207   std::set<int> emptySet;
12208   emptySet.clear();
12209   std::set<int> shapeIds;
12210
12211   SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12212   while (itelem->more())
12213   {
12214     const SMDS_MeshElement *elem = itelem->next();
12215     int shapeId = elem->getshapeId();
12216     int vtkId = elem->getVtkId();
12217     if (!shapeIdToVtkIdSet.count(shapeId))
12218     {
12219       shapeIdToVtkIdSet[shapeId] = emptySet;
12220       shapeIds.insert(shapeId);
12221     }
12222     shapeIdToVtkIdSet[shapeId].insert(vtkId);
12223   }
12224
12225   std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12226   std::set<DownIdType, DownIdCompare> emptyEdges;
12227   emptyEdges.clear();
12228
12229   std::map<int, std::set<int> >::iterator itShape =  shapeIdToVtkIdSet.begin();
12230   for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12231   {
12232     int shapeId = itShape->first;
12233     MESSAGE(" --- Shape ID --- "<< shapeId);
12234     shapeIdToEdges[shapeId] = emptyEdges;
12235
12236     std::vector<int> nodesEdges;
12237
12238     std::set<int>::iterator its = itShape->second.begin();
12239     for (; its != itShape->second.end(); ++its)
12240     {
12241       int vtkId = *its;
12242       MESSAGE("     " << vtkId);
12243       int neighborsVtkIds[NBMAXNEIGHBORS];
12244       int downIds[NBMAXNEIGHBORS];
12245       unsigned char downTypes[NBMAXNEIGHBORS];
12246       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12247       for (int n = 0; n < nbNeighbors; n++)
12248       {
12249         if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12250           continue;
12251         int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
12252         const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12253         if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12254         {
12255           DownIdType edge(downIds[n], downTypes[n]);
12256           if (!shapeIdToEdges[shapeId].count(edge))
12257           {
12258             shapeIdToEdges[shapeId].insert(edge);
12259             int vtkNodeId[3];
12260             int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12261             nodesEdges.push_back(vtkNodeId[0]);
12262             nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12263             MESSAGE("       --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12264           }
12265         }
12266       }
12267     }
12268
12269     std::list<int> order;
12270     order.clear();
12271     if (nodesEdges.size() > 0)
12272     {
12273       order.push_back(nodesEdges[0]); MESSAGE("       --- back " << order.back()+1); // SMDS id = VTK id + 1;
12274       nodesEdges[0] = -1;
12275       order.push_back(nodesEdges[1]); MESSAGE("       --- back " << order.back()+1);
12276       nodesEdges[1] = -1; // do not reuse this edge
12277       bool found = true;
12278       while (found)
12279       {
12280         int nodeTofind = order.back(); // try first to push back
12281         int i = 0;
12282         for ( i = 0; i < (int)nodesEdges.size(); i++ )
12283           if (nodesEdges[i] == nodeTofind)
12284             break;
12285         if ( i == (int) nodesEdges.size() )
12286           found = false; // no follower found on back
12287         else
12288         {
12289           if (i%2) // odd ==> use the previous one
12290             if (nodesEdges[i-1] < 0)
12291               found = false;
12292             else
12293             {
12294               order.push_back(nodesEdges[i-1]); MESSAGE("       --- back " << order.back()+1);
12295               nodesEdges[i-1] = -1;
12296             }
12297           else // even ==> use the next one
12298             if (nodesEdges[i+1] < 0)
12299               found = false;
12300             else
12301             {
12302               order.push_back(nodesEdges[i+1]); MESSAGE("       --- back " << order.back()+1);
12303               nodesEdges[i+1] = -1;
12304             }
12305         }
12306         if (found)
12307           continue;
12308         // try to push front
12309         found = true;
12310         nodeTofind = order.front(); // try to push front
12311         for ( i = 0;  i < (int)nodesEdges.size(); i++ )
12312           if ( nodesEdges[i] == nodeTofind )
12313             break;
12314         if ( i == (int)nodesEdges.size() )
12315         {
12316           found = false; // no predecessor found on front
12317           continue;
12318         }
12319         if (i%2) // odd ==> use the previous one
12320           if (nodesEdges[i-1] < 0)
12321             found = false;
12322           else
12323           {
12324             order.push_front(nodesEdges[i-1]); MESSAGE("       --- front " << order.front()+1);
12325             nodesEdges[i-1] = -1;
12326           }
12327         else // even ==> use the next one
12328           if (nodesEdges[i+1] < 0)
12329             found = false;
12330           else
12331           {
12332             order.push_front(nodesEdges[i+1]); MESSAGE("       --- front " << order.front()+1);
12333             nodesEdges[i+1] = -1;
12334           }
12335       }
12336     }
12337
12338
12339     std::vector<int> nodes;
12340     nodes.push_back(shapeId);
12341     std::list<int>::iterator itl = order.begin();
12342     for (; itl != order.end(); itl++)
12343     {
12344       nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12345       MESSAGE("              ordered node " << nodes[nodes.size()-1]);
12346     }
12347     listOfListOfNodes.push_back(nodes);
12348   }
12349
12350   //     partition geom faces with blocFissure
12351   //     mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12352   //     mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12353
12354   return;
12355 }
12356
12357
12358 //================================================================================
12359 /*!
12360  * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12361  * The created 2D mesh elements based on nodes of free faces of boundary volumes
12362  * \return TRUE if operation has been completed successfully, FALSE otherwise
12363  */
12364 //================================================================================
12365
12366 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12367 {
12368   // iterates on volume elements and detect all free faces on them
12369   SMESHDS_Mesh* aMesh = GetMeshDS();
12370   if (!aMesh)
12371     return false;
12372
12373   ElemFeatures faceType( SMDSAbs_Face );
12374   int nbFree = 0, nbExisted = 0, nbCreated = 0;
12375   SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12376   while(vIt->more())
12377   {
12378     const SMDS_MeshVolume* volume = vIt->next();
12379     SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12380     vTool.SetExternalNormal();
12381     const int iQuad = volume->IsQuadratic();
12382     faceType.SetQuad( iQuad );
12383     for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12384     {
12385       if (!vTool.IsFreeFace(iface))
12386         continue;
12387       nbFree++;
12388       vector<const SMDS_MeshNode *> nodes;
12389       int nbFaceNodes = vTool.NbFaceNodes(iface);
12390       const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12391       int inode = 0;
12392       for ( ; inode < nbFaceNodes; inode += iQuad+1)
12393         nodes.push_back(faceNodes[inode]);
12394
12395       if (iQuad) // add medium nodes
12396       {
12397         for ( inode = 1; inode < nbFaceNodes; inode += 2)
12398           nodes.push_back(faceNodes[inode]);
12399         if ( nbFaceNodes == 9 ) // bi-quadratic quad
12400           nodes.push_back(faceNodes[8]);
12401       }
12402       // add new face based on volume nodes
12403       if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) )
12404       {
12405         nbExisted++; // face already exsist
12406       }
12407       else
12408       {
12409         AddElement( nodes, faceType.SetPoly( nbFaceNodes/(iQuad+1) > 4 ));
12410         nbCreated++;
12411       }
12412     }
12413   }
12414   return ( nbFree == ( nbExisted + nbCreated ));
12415 }
12416
12417 namespace
12418 {
12419   inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12420   {
12421     if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12422       return n;
12423     return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12424   }
12425 }
12426 //================================================================================
12427 /*!
12428  * \brief Creates missing boundary elements
12429  *  \param elements - elements whose boundary is to be checked
12430  *  \param dimension - defines type of boundary elements to create
12431  *  \param group - a group to store created boundary elements in
12432  *  \param targetMesh - a mesh to store created boundary elements in
12433  *  \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12434  *  \param toCopyExistingBoundary - if true, not only new but also pre-existing
12435  *                                boundary elements will be copied into the targetMesh
12436  *  \param toAddExistingBondary - if true, not only new but also pre-existing
12437  *                                boundary elements will be added into the new group
12438  *  \param aroundElements - if true, elements will be created on boundary of given
12439  *                          elements else, on boundary of the whole mesh.
12440  * \return nb of added boundary elements
12441  */
12442 //================================================================================
12443
12444 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12445                                        Bnd_Dimension           dimension,
12446                                        SMESH_Group*            group/*=0*/,
12447                                        SMESH_Mesh*             targetMesh/*=0*/,
12448                                        bool                    toCopyElements/*=false*/,
12449                                        bool                    toCopyExistingBoundary/*=false*/,
12450                                        bool                    toAddExistingBondary/*= false*/,
12451                                        bool                    aroundElements/*= false*/)
12452 {
12453   SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12454   SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12455   // hope that all elements are of the same type, do not check them all
12456   if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12457     throw SALOME_Exception(LOCALIZED("wrong element type"));
12458
12459   if ( !targetMesh )
12460     toCopyElements = toCopyExistingBoundary = false;
12461
12462   SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12463   SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12464   int nbAddedBnd = 0;
12465
12466   // editor adding present bnd elements and optionally holding elements to add to the group
12467   SMESH_MeshEditor* presentEditor;
12468   SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12469   presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12470
12471   SMESH_MesherHelper helper( *myMesh );
12472   const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12473   SMDS_VolumeTool vTool;
12474   TIDSortedElemSet avoidSet;
12475   const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12476   size_t inode;
12477
12478   typedef vector<const SMDS_MeshNode*> TConnectivity;
12479   TConnectivity tgtNodes;
12480   ElemFeatures elemKind( missType ), elemToCopy;
12481
12482   vector<const SMDS_MeshElement*> presentBndElems;
12483   vector<TConnectivity>           missingBndElems;
12484   vector<int>                     freeFacets;
12485   TConnectivity nodes, elemNodes;
12486
12487   SMDS_ElemIteratorPtr eIt;
12488   if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12489   else                  eIt = elemSetIterator( elements );
12490
12491   while (eIt->more())
12492   {
12493     const SMDS_MeshElement* elem = eIt->next();
12494     const int              iQuad = elem->IsQuadratic();
12495     elemKind.SetQuad( iQuad );
12496
12497     // ------------------------------------------------------------------------------------
12498     // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12499     // ------------------------------------------------------------------------------------
12500     presentBndElems.clear();
12501     missingBndElems.clear();
12502     freeFacets.clear(); nodes.clear(); elemNodes.clear();
12503     if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12504     {
12505       const SMDS_MeshElement* otherVol = 0;
12506       for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12507       {
12508         if ( !vTool.IsFreeFace(iface, &otherVol) &&
12509              ( !aroundElements || elements.count( otherVol )))
12510           continue;
12511         freeFacets.push_back( iface );
12512       }
12513       if ( missType == SMDSAbs_Face )
12514         vTool.SetExternalNormal();
12515       for ( size_t i = 0; i < freeFacets.size(); ++i )
12516       {
12517         int                iface = freeFacets[i];
12518         const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12519         const size_t nbFaceNodes = vTool.NbFaceNodes (iface);
12520         if ( missType == SMDSAbs_Edge ) // boundary edges
12521         {
12522           nodes.resize( 2+iQuad );
12523           for ( size_t i = 0; i < nbFaceNodes; i += 1+iQuad )
12524           {
12525             for ( size_t j = 0; j < nodes.size(); ++j )
12526               nodes[ j ] = nn[ i+j ];
12527             if ( const SMDS_MeshElement* edge =
12528                  aMesh->FindElement( nodes, SMDSAbs_Edge, /*noMedium=*/false ))
12529               presentBndElems.push_back( edge );
12530             else
12531               missingBndElems.push_back( nodes );
12532           }
12533         }
12534         else // boundary face
12535         {
12536           nodes.clear();
12537           for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12538             nodes.push_back( nn[inode] ); // add corner nodes
12539           if (iQuad)
12540             for ( inode = 1; inode < nbFaceNodes; inode += 2)
12541               nodes.push_back( nn[inode] ); // add medium nodes
12542           int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12543           if ( iCenter > 0 )
12544             nodes.push_back( vTool.GetNodes()[ iCenter ] );
12545
12546           if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12547                                                                SMDSAbs_Face, /*noMedium=*/false ))
12548             presentBndElems.push_back( f );
12549           else
12550             missingBndElems.push_back( nodes );
12551
12552           if ( targetMesh != myMesh )
12553           {
12554             // add 1D elements on face boundary to be added to a new mesh
12555             const SMDS_MeshElement* edge;
12556             for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12557             {
12558               if ( iQuad )
12559                 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12560               else
12561                 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12562               if ( edge && avoidSet.insert( edge ).second )
12563                 presentBndElems.push_back( edge );
12564             }
12565           }
12566         }
12567       }
12568     }
12569     else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12570     {
12571       avoidSet.clear(), avoidSet.insert( elem );
12572       elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesElemIterator() ),
12573                         SMDS_MeshElement::iterator() );
12574       elemNodes.push_back( elemNodes[0] );
12575       nodes.resize( 2 + iQuad );
12576       const int nbLinks = elem->NbCornerNodes();
12577       for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12578       {
12579         nodes[0] = elemNodes[iN];
12580         nodes[1] = elemNodes[iN+1+iQuad];
12581         if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12582           continue; // not free link
12583
12584         if ( iQuad ) nodes[2] = elemNodes[iN+1];
12585         if ( const SMDS_MeshElement* edge =
12586              aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12587           presentBndElems.push_back( edge );
12588         else
12589           missingBndElems.push_back( nodes );
12590       }
12591     }
12592
12593     // ---------------------------------
12594     // 2. Add missing boundary elements
12595     // ---------------------------------
12596     if ( targetMesh != myMesh )
12597       // instead of making a map of nodes in this mesh and targetMesh,
12598       // we create nodes with same IDs.
12599       for ( size_t i = 0; i < missingBndElems.size(); ++i )
12600       {
12601         TConnectivity& srcNodes = missingBndElems[i];
12602         tgtNodes.resize( srcNodes.size() );
12603         for ( inode = 0; inode < srcNodes.size(); ++inode )
12604           tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12605         if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( tgtNodes,
12606                                                                    missType,
12607                                                                    /*noMedium=*/false))
12608           continue;
12609         tgtEditor.AddElement( tgtNodes, elemKind.SetPoly( tgtNodes.size()/(iQuad+1) > 4 ));
12610         ++nbAddedBnd;
12611       }
12612     else
12613       for ( size_t i = 0; i < missingBndElems.size(); ++i )
12614       {
12615         TConnectivity& nodes = missingBndElems[ i ];
12616         if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12617                                                                    missType,
12618                                                                    /*noMedium=*/false))
12619           continue;
12620         SMDS_MeshElement* newElem =
12621           tgtEditor.AddElement( nodes, elemKind.SetPoly( nodes.size()/(iQuad+1) > 4 ));
12622         nbAddedBnd += bool( newElem );
12623
12624         // try to set a new element to a shape
12625         if ( myMesh->HasShapeToMesh() )
12626         {
12627           bool ok = true;
12628           set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12629           const size_t nbN = nodes.size() / (iQuad+1 );
12630           for ( inode = 0; inode < nbN && ok; ++inode )
12631           {
12632             pair<int, TopAbs_ShapeEnum> i_stype =
12633               helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12634             if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12635               mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12636           }
12637           if ( ok && mediumShapes.size() > 1 )
12638           {
12639             set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12640             pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12641             for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12642             {
12643               if (( ok = ( stype_i->first != stype_i_0.first )))
12644                 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12645                                         aMesh->IndexToShape( stype_i_0.second ));
12646             }
12647           }
12648           if ( ok && mediumShapes.begin()->first == missShapeType )
12649             aMesh->SetMeshElementOnShape( newElem, mediumShapes.begin()->second );
12650         }
12651       }
12652
12653     // ----------------------------------
12654     // 3. Copy present boundary elements
12655     // ----------------------------------
12656     if ( toCopyExistingBoundary )
12657       for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12658       {
12659         const SMDS_MeshElement* e = presentBndElems[i];
12660         tgtNodes.resize( e->NbNodes() );
12661         for ( inode = 0; inode < tgtNodes.size(); ++inode )
12662           tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12663         presentEditor->AddElement( tgtNodes, elemToCopy.Init( e ));
12664       }
12665     else // store present elements to add them to a group
12666       for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12667       {
12668         presentEditor->myLastCreatedElems.Append( presentBndElems[ i ]);
12669       }
12670
12671   } // loop on given elements
12672
12673   // ---------------------------------------------
12674   // 4. Fill group with boundary elements
12675   // ---------------------------------------------
12676   if ( group )
12677   {
12678     if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12679       for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i )
12680         g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 ));
12681   }
12682   tgtEditor.myLastCreatedElems.Clear();
12683   tgtEditor2.myLastCreatedElems.Clear();
12684
12685   // -----------------------
12686   // 5. Copy given elements
12687   // -----------------------
12688   if ( toCopyElements && targetMesh != myMesh )
12689   {
12690     if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12691     else                  eIt = elemSetIterator( elements );
12692     while (eIt->more())
12693     {
12694       const SMDS_MeshElement* elem = eIt->next();
12695       tgtNodes.resize( elem->NbNodes() );
12696       for ( inode = 0; inode < tgtNodes.size(); ++inode )
12697         tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12698       tgtEditor.AddElement( tgtNodes, elemToCopy.Init( elem ));
12699
12700       tgtEditor.myLastCreatedElems.Clear();
12701     }
12702   }
12703   return nbAddedBnd;
12704 }
12705
12706 //================================================================================
12707 /*!
12708  * \brief Copy node position and set \a to node on the same geometry
12709  */
12710 //================================================================================
12711
12712 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
12713                                      const SMDS_MeshNode* to )
12714 {
12715   if ( !from || !to ) return;
12716
12717   SMDS_PositionPtr pos = from->GetPosition();
12718   if ( !pos || from->getshapeId() < 1 ) return;
12719
12720   switch ( pos->GetTypeOfPosition() )
12721   {
12722   case SMDS_TOP_3DSPACE: break;
12723
12724   case SMDS_TOP_FACE:
12725   {
12726     const SMDS_FacePosition* fPos = static_cast< const SMDS_FacePosition* >( pos );
12727     GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
12728                                 fPos->GetUParameter(), fPos->GetVParameter() );
12729     break;
12730   }
12731   case SMDS_TOP_EDGE:
12732   {
12733     // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
12734     const SMDS_EdgePosition* ePos = static_cast< const SMDS_EdgePosition* >( pos );
12735     GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
12736     break;
12737   }
12738   case SMDS_TOP_VERTEX:
12739   {
12740     GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
12741     break;
12742   }
12743   case SMDS_TOP_UNSPEC:
12744   default:;
12745   }
12746 }