Salome HOME
Regression of BelongToGeom on Debian-6
[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 int      theFlags,
5486                                             const double   theTolerance):
5487   myDir( theStep ),
5488   myFlags( theFlags ),
5489   myTolerance( theTolerance ),
5490   myElemsToUse( NULL )
5491 {
5492   mySteps = new TColStd_HSequenceOfReal;
5493   const double stepSize = theStep.Magnitude();
5494   for (int i=1; i<=theNbSteps; i++ )
5495     mySteps->Append( stepSize );
5496
5497   if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5498       ( theTolerance > 0 ))
5499   {
5500     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5501   }
5502   else
5503   {
5504     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5505   }
5506 }
5507
5508 //=======================================================================
5509 //function : ExtrusParam
5510 //purpose  : steps are given explicitly
5511 //=======================================================================
5512
5513 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Dir&                   theDir,
5514                                             Handle(TColStd_HSequenceOfReal) theSteps,
5515                                             const int                       theFlags,
5516                                             const double                    theTolerance):
5517   myDir( theDir ),
5518   mySteps( theSteps ),
5519   myFlags( theFlags ),
5520   myTolerance( theTolerance ),
5521   myElemsToUse( NULL )
5522 {
5523   if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5524       ( theTolerance > 0 ))
5525   {
5526     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5527   }
5528   else
5529   {
5530     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5531   }
5532 }
5533
5534 //=======================================================================
5535 //function : ExtrusParam
5536 //purpose  : for extrusion by normal
5537 //=======================================================================
5538
5539 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const double theStepSize,
5540                                             const int    theNbSteps,
5541                                             const int    theFlags,
5542                                             const int    theDim ):
5543   myDir( 1,0,0 ),
5544   mySteps( new TColStd_HSequenceOfReal ),
5545   myFlags( theFlags ),
5546   myTolerance( 0 ),
5547   myElemsToUse( NULL )
5548 {
5549   for (int i = 0; i < theNbSteps; i++ )
5550     mySteps->Append( theStepSize );
5551
5552   if ( theDim == 1 )
5553   {
5554     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal1D;
5555   }
5556   else
5557   {
5558     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal2D;
5559   }
5560 }
5561
5562 //=======================================================================
5563 //function : ExtrusParam::SetElementsToUse
5564 //purpose  : stores elements to use for extrusion by normal, depending on
5565 //           state of EXTRUSION_FLAG_USE_INPUT_ELEMS_ONLY flag
5566 //=======================================================================
5567
5568 void SMESH_MeshEditor::ExtrusParam::SetElementsToUse( const TIDSortedElemSet& elems )
5569 {
5570   myElemsToUse = ToUseInpElemsOnly() ? & elems : 0;
5571 }
5572
5573 //=======================================================================
5574 //function : ExtrusParam::beginStepIter
5575 //purpose  : prepare iteration on steps
5576 //=======================================================================
5577
5578 void SMESH_MeshEditor::ExtrusParam::beginStepIter( bool withMediumNodes )
5579 {
5580   myWithMediumNodes = withMediumNodes;
5581   myNextStep = 1;
5582   myCurSteps.clear();
5583 }
5584 //=======================================================================
5585 //function : ExtrusParam::moreSteps
5586 //purpose  : are there more steps?
5587 //=======================================================================
5588
5589 bool SMESH_MeshEditor::ExtrusParam::moreSteps()
5590 {
5591   return myNextStep <= mySteps->Length() || !myCurSteps.empty();
5592 }
5593 //=======================================================================
5594 //function : ExtrusParam::nextStep
5595 //purpose  : returns the next step
5596 //=======================================================================
5597
5598 double SMESH_MeshEditor::ExtrusParam::nextStep()
5599 {
5600   double res = 0;
5601   if ( !myCurSteps.empty() )
5602   {
5603     res = myCurSteps.back();
5604     myCurSteps.pop_back();
5605   }
5606   else if ( myNextStep <= mySteps->Length() )
5607   {
5608     myCurSteps.push_back( mySteps->Value( myNextStep ));
5609     ++myNextStep;
5610     if ( myWithMediumNodes )
5611     {
5612       myCurSteps.back() /= 2.;
5613       myCurSteps.push_back( myCurSteps.back() );
5614     }
5615     res = nextStep();
5616   }
5617   return res;
5618 }
5619
5620 //=======================================================================
5621 //function : ExtrusParam::makeNodesByDir
5622 //purpose  : create nodes for standard extrusion
5623 //=======================================================================
5624
5625 int SMESH_MeshEditor::ExtrusParam::
5626 makeNodesByDir( SMESHDS_Mesh*                     mesh,
5627                 const SMDS_MeshNode*              srcNode,
5628                 std::list<const SMDS_MeshNode*> & newNodes,
5629                 const bool                        makeMediumNodes)
5630 {
5631   gp_XYZ p = SMESH_TNodeXYZ( srcNode );
5632
5633   int nbNodes = 0;
5634   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5635   {
5636     p += myDir.XYZ() * nextStep();
5637     const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5638     newNodes.push_back( newNode );
5639   }
5640   return nbNodes;
5641 }
5642
5643 //=======================================================================
5644 //function : ExtrusParam::makeNodesByDirAndSew
5645 //purpose  : create nodes for standard extrusion with sewing
5646 //=======================================================================
5647
5648 int SMESH_MeshEditor::ExtrusParam::
5649 makeNodesByDirAndSew( SMESHDS_Mesh*                     mesh,
5650                       const SMDS_MeshNode*              srcNode,
5651                       std::list<const SMDS_MeshNode*> & newNodes,
5652                       const bool                        makeMediumNodes)
5653 {
5654   gp_XYZ P1 = SMESH_TNodeXYZ( srcNode );
5655
5656   int nbNodes = 0;
5657   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5658   {
5659     P1 += myDir.XYZ() * nextStep();
5660
5661     // try to search in sequence of existing nodes
5662     // if myNodes.Length()>0 we 'nave to use given sequence
5663     // else - use all nodes of mesh
5664     const SMDS_MeshNode * node = 0;
5665     if ( myNodes.Length() > 0 ) {
5666       int i;
5667       for(i=1; i<=myNodes.Length(); i++) {
5668         gp_XYZ P2 = SMESH_TNodeXYZ( myNodes.Value(i) );
5669         if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5670         {
5671           node = myNodes.Value(i);
5672           break;
5673         }
5674       }
5675     }
5676     else {
5677       SMDS_NodeIteratorPtr itn = mesh->nodesIterator();
5678       while(itn->more()) {
5679         SMESH_TNodeXYZ P2( itn->next() );
5680         if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5681         {
5682           node = P2._node;
5683           break;
5684         }
5685       }
5686     }
5687
5688     if ( !node )
5689       node = mesh->AddNode( P1.X(), P1.Y(), P1.Z() );
5690
5691     newNodes.push_back( node );
5692
5693   } // loop on steps
5694
5695   return nbNodes;
5696 }
5697
5698 //=======================================================================
5699 //function : ExtrusParam::makeNodesByNormal2D
5700 //purpose  : create nodes for extrusion using normals of faces
5701 //=======================================================================
5702
5703 int SMESH_MeshEditor::ExtrusParam::
5704 makeNodesByNormal2D( SMESHDS_Mesh*                     mesh,
5705                      const SMDS_MeshNode*              srcNode,
5706                      std::list<const SMDS_MeshNode*> & newNodes,
5707                      const bool                        makeMediumNodes)
5708 {
5709   const bool alongAvgNorm = ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL );
5710
5711   gp_XYZ p = SMESH_TNodeXYZ( srcNode );
5712
5713   // get normals to faces sharing srcNode
5714   vector< gp_XYZ > norms, baryCenters;
5715   gp_XYZ norm, avgNorm( 0,0,0 );
5716   SMDS_ElemIteratorPtr faceIt = srcNode->GetInverseElementIterator( SMDSAbs_Face );
5717   while ( faceIt->more() )
5718   {
5719     const SMDS_MeshElement* face = faceIt->next();
5720     if ( myElemsToUse && !myElemsToUse->count( face ))
5721       continue;
5722     if ( SMESH_MeshAlgos::FaceNormal( face, norm, /*normalized=*/true ))
5723     {
5724       norms.push_back( norm );
5725       avgNorm += norm;
5726       if ( !alongAvgNorm )
5727       {
5728         gp_XYZ bc(0,0,0);
5729         int nbN = 0;
5730         for ( SMDS_ElemIteratorPtr nIt = face->nodesIterator(); nIt->more(); ++nbN )
5731           bc += SMESH_TNodeXYZ( nIt->next() );
5732         baryCenters.push_back( bc / nbN );
5733       }
5734     }
5735   }
5736
5737   if ( norms.empty() ) return 0;
5738
5739   double normSize = avgNorm.Modulus();
5740   if ( normSize < std::numeric_limits<double>::min() )
5741     return 0;
5742
5743   if ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL ) // extrude along avgNorm
5744   {
5745     myDir = avgNorm;
5746     return makeNodesByDir( mesh, srcNode, newNodes, makeMediumNodes );
5747   }
5748
5749   avgNorm /= normSize;
5750
5751   int nbNodes = 0;
5752   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5753   {
5754     gp_XYZ pNew = p;
5755     double stepSize = nextStep();
5756
5757     if ( norms.size() > 1 )
5758     {
5759       for ( size_t iF = 0; iF < norms.size(); ++iF ) // loop on faces
5760       {
5761         // translate plane of a face
5762         baryCenters[ iF ] += norms[ iF ] * stepSize;
5763
5764         // find point of intersection of the face plane located at baryCenters[ iF ]
5765         // and avgNorm located at pNew
5766         double d    = -( norms[ iF ] * baryCenters[ iF ]); // d of plane equation ax+by+cz+d=0
5767         double dot  = ( norms[ iF ] * avgNorm );
5768         if ( dot < std::numeric_limits<double>::min() )
5769           dot = stepSize * 1e-3;
5770         double step = -( norms[ iF ] * pNew + d ) / dot;
5771         pNew += step * avgNorm;
5772       }
5773     }
5774     else
5775     {
5776       pNew += stepSize * avgNorm;
5777     }
5778     p = pNew;
5779
5780     const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5781     newNodes.push_back( newNode );
5782   }
5783   return nbNodes;
5784 }
5785
5786 //=======================================================================
5787 //function : ExtrusParam::makeNodesByNormal1D
5788 //purpose  : create nodes for extrusion using normals of edges
5789 //=======================================================================
5790
5791 int SMESH_MeshEditor::ExtrusParam::
5792 makeNodesByNormal1D( SMESHDS_Mesh*                     mesh,
5793                      const SMDS_MeshNode*              srcNode,
5794                      std::list<const SMDS_MeshNode*> & newNodes,
5795                      const bool                        makeMediumNodes)
5796 {
5797   throw SALOME_Exception("Extrusion 1D by Normal not implemented");
5798   return 0;
5799 }
5800
5801 //=======================================================================
5802 //function : ExtrusionSweep
5803 //purpose  :
5804 //=======================================================================
5805
5806 SMESH_MeshEditor::PGroupIDs
5807 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet     theElems[2],
5808                                   const gp_Vec&        theStep,
5809                                   const int            theNbSteps,
5810                                   TTElemOfElemListMap& newElemsMap,
5811                                   const int            theFlags,
5812                                   const double         theTolerance)
5813 {
5814   ExtrusParam aParams( theStep, theNbSteps, theFlags, theTolerance );
5815   return ExtrusionSweep( theElems, aParams, newElemsMap );
5816 }
5817
5818
5819 //=======================================================================
5820 //function : ExtrusionSweep
5821 //purpose  :
5822 //=======================================================================
5823
5824 SMESH_MeshEditor::PGroupIDs
5825 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet     theElemSets[2],
5826                                   ExtrusParam&         theParams,
5827                                   TTElemOfElemListMap& newElemsMap)
5828 {
5829   myLastCreatedElems.Clear();
5830   myLastCreatedNodes.Clear();
5831
5832   // source elements for each generated one
5833   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5834
5835   //SMESHDS_Mesh* aMesh = GetMeshDS();
5836
5837   setElemsFirst( theElemSets );
5838   const int nbSteps = theParams.NbSteps();
5839   theParams.SetElementsToUse( theElemSets[0] );
5840
5841   TNodeOfNodeListMap mapNewNodes;
5842   //TNodeOfNodeVecMap mapNewNodes;
5843   TElemOfVecOfNnlmiMap mapElemNewNodes;
5844   //TElemOfVecOfMapNodesMap mapElemNewNodes;
5845
5846   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5847                                      myMesh->NbFaces(ORDER_QUADRATIC) +
5848                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
5849   // loop on theElems
5850   TIDSortedElemSet::iterator itElem;
5851   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5852   {
5853     TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5854     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5855     {
5856       // check element type
5857       const SMDS_MeshElement* elem = *itElem;
5858       if ( !elem  || elem->GetType() == SMDSAbs_Volume )
5859         continue;
5860
5861       const size_t nbNodes = elem->NbNodes();
5862       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5863       newNodesItVec.reserve( nbNodes );
5864
5865       // loop on elem nodes
5866       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5867       while ( itN->more() )
5868       {
5869         // check if a node has been already sweeped
5870         const SMDS_MeshNode* node = cast2Node( itN->next() );
5871         TNodeOfNodeListMap::iterator nIt =
5872           mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5873         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5874         if ( listNewNodes.empty() )
5875         {
5876           // make new nodes
5877
5878           // check if we are to create medium nodes between corner ones
5879           bool needMediumNodes = false;
5880           if ( isQuadraticMesh )
5881           {
5882             SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5883             while (it->more() && !needMediumNodes )
5884             {
5885               const SMDS_MeshElement* invElem = it->next();
5886               if ( invElem != elem && !theElems.count( invElem )) continue;
5887               needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5888               if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5889                 needMediumNodes = true;
5890             }
5891           }
5892           // create nodes for all steps
5893           if ( theParams.MakeNodes( GetMeshDS(), node, listNewNodes, needMediumNodes ))
5894           {
5895             list<const SMDS_MeshNode*>::iterator newNodesIt = listNewNodes.begin();
5896             for ( ; newNodesIt != listNewNodes.end(); ++newNodesIt )
5897             {
5898               myLastCreatedNodes.Append( *newNodesIt );
5899               srcNodes.Append( node );
5900             }
5901           }
5902           else
5903           {
5904             break; // newNodesItVec will be shorter than nbNodes
5905           }
5906         }
5907         newNodesItVec.push_back( nIt );
5908       }
5909       // make new elements
5910       if ( newNodesItVec.size() == nbNodes )
5911         sweepElement( elem, newNodesItVec, newElemsMap[elem], nbSteps, srcElems );
5912     }
5913   }
5914
5915   if ( theParams.ToMakeBoundary() ) {
5916     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], nbSteps, srcElems );
5917   }
5918   PGroupIDs newGroupIDs;
5919   if ( theParams.ToMakeGroups() )
5920     newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
5921
5922   return newGroupIDs;
5923 }
5924
5925 //=======================================================================
5926 //function : ExtrusionAlongTrack
5927 //purpose  :
5928 //=======================================================================
5929 SMESH_MeshEditor::Extrusion_Error
5930 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet     theElements[2],
5931                                        SMESH_subMesh*       theTrack,
5932                                        const SMDS_MeshNode* theN1,
5933                                        const bool           theHasAngles,
5934                                        list<double>&        theAngles,
5935                                        const bool           theLinearVariation,
5936                                        const bool           theHasRefPoint,
5937                                        const gp_Pnt&        theRefPoint,
5938                                        const bool           theMakeGroups)
5939 {
5940   myLastCreatedElems.Clear();
5941   myLastCreatedNodes.Clear();
5942
5943   int aNbE;
5944   std::list<double> aPrms;
5945   TIDSortedElemSet::iterator itElem;
5946
5947   gp_XYZ aGC;
5948   TopoDS_Edge aTrackEdge;
5949   TopoDS_Vertex aV1, aV2;
5950
5951   SMDS_ElemIteratorPtr aItE;
5952   SMDS_NodeIteratorPtr aItN;
5953   SMDSAbs_ElementType aTypeE;
5954
5955   TNodeOfNodeListMap mapNewNodes;
5956
5957   // 1. Check data
5958   aNbE = theElements[0].size() + theElements[1].size();
5959   // nothing to do
5960   if ( !aNbE )
5961     return EXTR_NO_ELEMENTS;
5962
5963   // 1.1 Track Pattern
5964   ASSERT( theTrack );
5965
5966   SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS();
5967   if ( !pSubMeshDS )
5968     return ExtrusionAlongTrack( theElements, theTrack->GetFather(), theN1,
5969                                 theHasAngles, theAngles, theLinearVariation,
5970                                 theHasRefPoint, theRefPoint, theMakeGroups );
5971
5972   aItE = pSubMeshDS->GetElements();
5973   while ( aItE->more() ) {
5974     const SMDS_MeshElement* pE = aItE->next();
5975     aTypeE = pE->GetType();
5976     // Pattern must contain links only
5977     if ( aTypeE != SMDSAbs_Edge )
5978       return EXTR_PATH_NOT_EDGE;
5979   }
5980
5981   list<SMESH_MeshEditor_PathPoint> fullList;
5982
5983   const TopoDS_Shape& aS = theTrack->GetSubShape();
5984   // Sub-shape for the Pattern must be an Edge or Wire
5985   if( aS.ShapeType() == TopAbs_EDGE ) {
5986     aTrackEdge = TopoDS::Edge( aS );
5987     // the Edge must not be degenerated
5988     if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
5989       return EXTR_BAD_PATH_SHAPE;
5990     TopExp::Vertices( aTrackEdge, aV1, aV2 );
5991     aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
5992     const SMDS_MeshNode* aN1 = aItN->next();
5993     aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
5994     const SMDS_MeshNode* aN2 = aItN->next();
5995     // starting node must be aN1 or aN2
5996     if ( !( aN1 == theN1 || aN2 == theN1 ) )
5997       return EXTR_BAD_STARTING_NODE;
5998     aItN = pSubMeshDS->GetNodes();
5999     while ( aItN->more() ) {
6000       const SMDS_MeshNode* pNode = aItN->next();
6001       const SMDS_EdgePosition* pEPos =
6002         static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6003       double aT = pEPos->GetUParameter();
6004       aPrms.push_back( aT );
6005     }
6006     //Extrusion_Error err =
6007     MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
6008   } else if( aS.ShapeType() == TopAbs_WIRE ) {
6009     list< SMESH_subMesh* > LSM;
6010     TopTools_SequenceOfShape Edges;
6011     SMESH_subMeshIteratorPtr itSM = theTrack->getDependsOnIterator(false,true);
6012     while(itSM->more()) {
6013       SMESH_subMesh* SM = itSM->next();
6014       LSM.push_back(SM);
6015       const TopoDS_Shape& aS = SM->GetSubShape();
6016       Edges.Append(aS);
6017     }
6018     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6019     int startNid = theN1->GetID();
6020     TColStd_MapOfInteger UsedNums;
6021
6022     int NbEdges = Edges.Length();
6023     int i = 1;
6024     for(; i<=NbEdges; i++) {
6025       int k = 0;
6026       list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
6027       for(; itLSM!=LSM.end(); itLSM++) {
6028         k++;
6029         if(UsedNums.Contains(k)) continue;
6030         aTrackEdge = TopoDS::Edge( Edges.Value(k) );
6031         SMESH_subMesh* locTrack = *itLSM;
6032         SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
6033         TopExp::Vertices( aTrackEdge, aV1, aV2 );
6034         aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
6035         const SMDS_MeshNode* aN1 = aItN->next();
6036         aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
6037         const SMDS_MeshNode* aN2 = aItN->next();
6038         // starting node must be aN1 or aN2
6039         if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
6040         // 2. Collect parameters on the track edge
6041         aPrms.clear();
6042         aItN = locMeshDS->GetNodes();
6043         while ( aItN->more() ) {
6044           const SMDS_MeshNode* pNode = aItN->next();
6045           const SMDS_EdgePosition* pEPos =
6046             static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6047           double aT = pEPos->GetUParameter();
6048           aPrms.push_back( aT );
6049         }
6050         list<SMESH_MeshEditor_PathPoint> LPP;
6051         //Extrusion_Error err =
6052         MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
6053         LLPPs.push_back(LPP);
6054         UsedNums.Add(k);
6055         // update startN for search following egde
6056         if( aN1->GetID() == startNid ) startNid = aN2->GetID();
6057         else startNid = aN1->GetID();
6058         break;
6059       }
6060     }
6061     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6062     list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
6063     list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
6064     for(; itPP!=firstList.end(); itPP++) {
6065       fullList.push_back( *itPP );
6066     }
6067     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6068     fullList.pop_back();
6069     itLLPP++;
6070     for(; itLLPP!=LLPPs.end(); itLLPP++) {
6071       list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
6072       itPP = currList.begin();
6073       SMESH_MeshEditor_PathPoint PP2 = currList.front();
6074       gp_Dir D1 = PP1.Tangent();
6075       gp_Dir D2 = PP2.Tangent();
6076       gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
6077                            (D1.Z()+D2.Z())/2 ) );
6078       PP1.SetTangent(Dnew);
6079       fullList.push_back(PP1);
6080       itPP++;
6081       for(; itPP!=firstList.end(); itPP++) {
6082         fullList.push_back( *itPP );
6083       }
6084       PP1 = fullList.back();
6085       fullList.pop_back();
6086     }
6087     // if wire not closed
6088     fullList.push_back(PP1);
6089     // else ???
6090   }
6091   else {
6092     return EXTR_BAD_PATH_SHAPE;
6093   }
6094
6095   return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
6096                           theHasRefPoint, theRefPoint, theMakeGroups);
6097 }
6098
6099
6100 //=======================================================================
6101 //function : ExtrusionAlongTrack
6102 //purpose  :
6103 //=======================================================================
6104 SMESH_MeshEditor::Extrusion_Error
6105 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet     theElements[2],
6106                                        SMESH_Mesh*          theTrack,
6107                                        const SMDS_MeshNode* theN1,
6108                                        const bool           theHasAngles,
6109                                        list<double>&        theAngles,
6110                                        const bool           theLinearVariation,
6111                                        const bool           theHasRefPoint,
6112                                        const gp_Pnt&        theRefPoint,
6113                                        const bool           theMakeGroups)
6114 {
6115   myLastCreatedElems.Clear();
6116   myLastCreatedNodes.Clear();
6117
6118   int aNbE;
6119   std::list<double> aPrms;
6120   TIDSortedElemSet::iterator itElem;
6121
6122   gp_XYZ aGC;
6123   TopoDS_Edge aTrackEdge;
6124   TopoDS_Vertex aV1, aV2;
6125
6126   SMDS_ElemIteratorPtr aItE;
6127   SMDS_NodeIteratorPtr aItN;
6128   SMDSAbs_ElementType aTypeE;
6129
6130   TNodeOfNodeListMap mapNewNodes;
6131
6132   // 1. Check data
6133   aNbE = theElements[0].size() + theElements[1].size();
6134   // nothing to do
6135   if ( !aNbE )
6136     return EXTR_NO_ELEMENTS;
6137
6138   // 1.1 Track Pattern
6139   ASSERT( theTrack );
6140
6141   SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS();
6142
6143   aItE = pMeshDS->elementsIterator();
6144   while ( aItE->more() ) {
6145     const SMDS_MeshElement* pE = aItE->next();
6146     aTypeE = pE->GetType();
6147     // Pattern must contain links only
6148     if ( aTypeE != SMDSAbs_Edge )
6149       return EXTR_PATH_NOT_EDGE;
6150   }
6151
6152   list<SMESH_MeshEditor_PathPoint> fullList;
6153
6154   const TopoDS_Shape& aS = theTrack->GetShapeToMesh();
6155
6156   if ( !theTrack->HasShapeToMesh() ) {
6157     //Mesh without shape
6158     const SMDS_MeshNode* currentNode = NULL;
6159     const SMDS_MeshNode* prevNode = theN1;
6160     std::vector<const SMDS_MeshNode*> aNodesList;
6161     aNodesList.push_back(theN1);
6162     int nbEdges = 0, conn=0;
6163     const SMDS_MeshElement* prevElem = NULL;
6164     const SMDS_MeshElement* currentElem = NULL;
6165     int totalNbEdges = theTrack->NbEdges();
6166     SMDS_ElemIteratorPtr nIt;
6167
6168     //check start node
6169     if( !theTrack->GetMeshDS()->Contains(theN1) ) {
6170       return EXTR_BAD_STARTING_NODE;
6171     }
6172
6173     conn = nbEdgeConnectivity(theN1);
6174     if( conn != 1 )
6175       return EXTR_PATH_NOT_EDGE;
6176
6177     aItE = theN1->GetInverseElementIterator();
6178     prevElem = aItE->next();
6179     currentElem = prevElem;
6180     //Get all nodes
6181     if(totalNbEdges == 1 ) {
6182       nIt = currentElem->nodesIterator();
6183       currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6184       if(currentNode == prevNode)
6185         currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6186       aNodesList.push_back(currentNode);
6187     } else {
6188       nIt = currentElem->nodesIterator();
6189       while( nIt->more() ) {
6190         currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6191         if(currentNode == prevNode)
6192           currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6193         aNodesList.push_back(currentNode);
6194
6195         //case of the closed mesh
6196         if(currentNode == theN1) {
6197           nbEdges++;
6198           break;
6199         }
6200
6201         conn = nbEdgeConnectivity(currentNode);
6202         if(conn > 2) {
6203           return EXTR_PATH_NOT_EDGE;
6204         }else if( conn == 1 && nbEdges > 0 ) {
6205           //End of the path
6206           nbEdges++;
6207           break;
6208         }else {
6209           prevNode = currentNode;
6210           aItE = currentNode->GetInverseElementIterator();
6211           currentElem = aItE->next();
6212           if( currentElem  == prevElem)
6213             currentElem = aItE->next();
6214           nIt = currentElem->nodesIterator();
6215           prevElem = currentElem;
6216           nbEdges++;
6217         }
6218       }
6219     }
6220
6221     if(nbEdges != totalNbEdges)
6222       return EXTR_PATH_NOT_EDGE;
6223
6224     TopTools_SequenceOfShape Edges;
6225     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6226     int startNid = theN1->GetID();
6227     for ( size_t i = 1; i < aNodesList.size(); i++ )
6228     {
6229       gp_Pnt     p1 = SMESH_TNodeXYZ( aNodesList[i-1] );
6230       gp_Pnt     p2 = SMESH_TNodeXYZ( aNodesList[i] );
6231       TopoDS_Edge e = BRepBuilderAPI_MakeEdge( p1, p2 );
6232       list<SMESH_MeshEditor_PathPoint> LPP;
6233       aPrms.clear();
6234       MakeEdgePathPoints(aPrms, e, (aNodesList[i-1]->GetID()==startNid), LPP);
6235       LLPPs.push_back(LPP);
6236       if ( aNodesList[i-1]->GetID() == startNid ) startNid = aNodesList[i  ]->GetID();
6237       else                                        startNid = aNodesList[i-1]->GetID();
6238     }
6239
6240     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6241     list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
6242     list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
6243     for(; itPP!=firstList.end(); itPP++) {
6244       fullList.push_back( *itPP );
6245     }
6246
6247     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6248     SMESH_MeshEditor_PathPoint PP2;
6249     fullList.pop_back();
6250     itLLPP++;
6251     for(; itLLPP!=LLPPs.end(); itLLPP++) {
6252       list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
6253       itPP = currList.begin();
6254       PP2 = currList.front();
6255       gp_Dir D1 = PP1.Tangent();
6256       gp_Dir D2 = PP2.Tangent();
6257       gp_Dir Dnew( 0.5 * ( D1.XYZ() + D2.XYZ() ));
6258       PP1.SetTangent(Dnew);
6259       fullList.push_back(PP1);
6260       itPP++;
6261       for(; itPP!=currList.end(); itPP++) {
6262         fullList.push_back( *itPP );
6263       }
6264       PP1 = fullList.back();
6265       fullList.pop_back();
6266     }
6267     fullList.push_back(PP1);
6268
6269   } // Sub-shape for the Pattern must be an Edge or Wire
6270   else if ( aS.ShapeType() == TopAbs_EDGE )
6271   {
6272     aTrackEdge = TopoDS::Edge( aS );
6273     // the Edge must not be degenerated
6274     if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
6275       return EXTR_BAD_PATH_SHAPE;
6276     TopExp::Vertices( aTrackEdge, aV1, aV2 );
6277     const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6278     const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6279     // starting node must be aN1 or aN2
6280     if ( !( aN1 == theN1 || aN2 == theN1 ) )
6281       return EXTR_BAD_STARTING_NODE;
6282     aItN = pMeshDS->nodesIterator();
6283     while ( aItN->more() ) {
6284       const SMDS_MeshNode* pNode = aItN->next();
6285       if( pNode==aN1 || pNode==aN2 ) continue;
6286       const SMDS_EdgePosition* pEPos =
6287         static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6288       double aT = pEPos->GetUParameter();
6289       aPrms.push_back( aT );
6290     }
6291     //Extrusion_Error err =
6292     MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
6293   }
6294   else if( aS.ShapeType() == TopAbs_WIRE ) {
6295     list< SMESH_subMesh* > LSM;
6296     TopTools_SequenceOfShape Edges;
6297     TopExp_Explorer eExp(aS, TopAbs_EDGE);
6298     for(; eExp.More(); eExp.Next()) {
6299       TopoDS_Edge E = TopoDS::Edge( eExp.Current() );
6300       if( SMESH_Algo::isDegenerated(E) ) continue;
6301       SMESH_subMesh* SM = theTrack->GetSubMesh(E);
6302       if(SM) {
6303         LSM.push_back(SM);
6304         Edges.Append(E);
6305       }
6306     }
6307     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6308     TopoDS_Vertex aVprev;
6309     TColStd_MapOfInteger UsedNums;
6310     int NbEdges = Edges.Length();
6311     int i = 1;
6312     for(; i<=NbEdges; i++) {
6313       int k = 0;
6314       list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
6315       for(; itLSM!=LSM.end(); itLSM++) {
6316         k++;
6317         if(UsedNums.Contains(k)) continue;
6318         aTrackEdge = TopoDS::Edge( Edges.Value(k) );
6319         SMESH_subMesh* locTrack = *itLSM;
6320         SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
6321         TopExp::Vertices( aTrackEdge, aV1, aV2 );
6322         bool aN1isOK = false, aN2isOK = false;
6323         if ( aVprev.IsNull() ) {
6324           // if previous vertex is not yet defined, it means that we in the beginning of wire
6325           // and we have to find initial vertex corresponding to starting node theN1
6326           const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6327           const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6328           // starting node must be aN1 or aN2
6329           aN1isOK = ( aN1 && aN1 == theN1 );
6330           aN2isOK = ( aN2 && aN2 == theN1 );
6331         }
6332         else {
6333           // we have specified ending vertex of the previous edge on the previous iteration
6334           // and we have just to check that it corresponds to any vertex in current segment
6335           aN1isOK = aVprev.IsSame( aV1 );
6336           aN2isOK = aVprev.IsSame( aV2 );
6337         }
6338         if ( !aN1isOK && !aN2isOK ) continue;
6339         // 2. Collect parameters on the track edge
6340         aPrms.clear();
6341         aItN = locMeshDS->GetNodes();
6342         while ( aItN->more() ) {
6343           const SMDS_MeshNode*     pNode = aItN->next();
6344           const SMDS_EdgePosition* pEPos =
6345             static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6346           double aT = pEPos->GetUParameter();
6347           aPrms.push_back( aT );
6348         }
6349         list<SMESH_MeshEditor_PathPoint> LPP;
6350         //Extrusion_Error err =
6351         MakeEdgePathPoints(aPrms, aTrackEdge, aN1isOK, LPP);
6352         LLPPs.push_back(LPP);
6353         UsedNums.Add(k);
6354         // update startN for search following egde
6355         if ( aN1isOK ) aVprev = aV2;
6356         else           aVprev = aV1;
6357         break;
6358       }
6359     }
6360     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6361     list<SMESH_MeshEditor_PathPoint>& firstList = *itLLPP;
6362     fullList.splice( fullList.end(), firstList );
6363
6364     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6365     fullList.pop_back();
6366     itLLPP++;
6367     for(; itLLPP!=LLPPs.end(); itLLPP++) {
6368       list<SMESH_MeshEditor_PathPoint>& currList = *itLLPP;
6369       SMESH_MeshEditor_PathPoint PP2 = currList.front();
6370       gp_Dir D1 = PP1.Tangent();
6371       gp_Dir D2 = PP2.Tangent();
6372       gp_Dir Dnew( D1.XYZ() + D2.XYZ() );
6373       PP1.SetTangent(Dnew);
6374       fullList.push_back(PP1);
6375       fullList.splice( fullList.end(), currList, ++currList.begin(), currList.end() );
6376       PP1 = fullList.back();
6377       fullList.pop_back();
6378     }
6379     // if wire not closed
6380     fullList.push_back(PP1);
6381     // else ???
6382   }
6383   else {
6384     return EXTR_BAD_PATH_SHAPE;
6385   }
6386
6387   return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
6388                           theHasRefPoint, theRefPoint, theMakeGroups);
6389 }
6390
6391
6392 //=======================================================================
6393 //function : MakeEdgePathPoints
6394 //purpose  : auxilary for ExtrusionAlongTrack
6395 //=======================================================================
6396 SMESH_MeshEditor::Extrusion_Error
6397 SMESH_MeshEditor::MakeEdgePathPoints(std::list<double>&                aPrms,
6398                                      const TopoDS_Edge&                aTrackEdge,
6399                                      bool                              FirstIsStart,
6400                                      list<SMESH_MeshEditor_PathPoint>& LPP)
6401 {
6402   Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2;
6403   aTolVec=1.e-7;
6404   aTolVec2=aTolVec*aTolVec;
6405   double aT1, aT2;
6406   TopoDS_Vertex aV1, aV2;
6407   TopExp::Vertices( aTrackEdge, aV1, aV2 );
6408   aT1=BRep_Tool::Parameter( aV1, aTrackEdge );
6409   aT2=BRep_Tool::Parameter( aV2, aTrackEdge );
6410   // 2. Collect parameters on the track edge
6411   aPrms.push_front( aT1 );
6412   aPrms.push_back( aT2 );
6413   // sort parameters
6414   aPrms.sort();
6415   if( FirstIsStart ) {
6416     if ( aT1 > aT2 ) {
6417       aPrms.reverse();
6418     }
6419   }
6420   else {
6421     if ( aT2 > aT1 ) {
6422       aPrms.reverse();
6423     }
6424   }
6425   // 3. Path Points
6426   SMESH_MeshEditor_PathPoint aPP;
6427   Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 );
6428   std::list<double>::iterator aItD = aPrms.begin();
6429   for(; aItD != aPrms.end(); ++aItD) {
6430     double aT = *aItD;
6431     gp_Pnt aP3D;
6432     gp_Vec aVec;
6433     aC3D->D1( aT, aP3D, aVec );
6434     aL2 = aVec.SquareMagnitude();
6435     if ( aL2 < aTolVec2 )
6436       return EXTR_CANT_GET_TANGENT;
6437     gp_Dir aTgt( FirstIsStart ? aVec : -aVec );
6438     aPP.SetPnt( aP3D );
6439     aPP.SetTangent( aTgt );
6440     aPP.SetParameter( aT );
6441     LPP.push_back(aPP);
6442   }
6443   return EXTR_OK;
6444 }
6445
6446
6447 //=======================================================================
6448 //function : MakeExtrElements
6449 //purpose  : auxilary for ExtrusionAlongTrack
6450 //=======================================================================
6451 SMESH_MeshEditor::Extrusion_Error
6452 SMESH_MeshEditor::MakeExtrElements(TIDSortedElemSet                  theElemSets[2],
6453                                    list<SMESH_MeshEditor_PathPoint>& fullList,
6454                                    const bool                        theHasAngles,
6455                                    list<double>&                     theAngles,
6456                                    const bool                        theLinearVariation,
6457                                    const bool                        theHasRefPoint,
6458                                    const gp_Pnt&                     theRefPoint,
6459                                    const bool                        theMakeGroups)
6460 {
6461   const int aNbTP = fullList.size();
6462
6463   // Angles
6464   if( theHasAngles && !theAngles.empty() && theLinearVariation )
6465     LinearAngleVariation(aNbTP-1, theAngles);
6466
6467   // fill vector of path points with angles
6468   vector<SMESH_MeshEditor_PathPoint> aPPs;
6469   list<SMESH_MeshEditor_PathPoint>::iterator itPP = fullList.begin();
6470   list<double>::iterator                 itAngles = theAngles.begin();
6471   aPPs.push_back( *itPP++ );
6472   for( ; itPP != fullList.end(); itPP++) {
6473     aPPs.push_back( *itPP );
6474     if ( theHasAngles && itAngles != theAngles.end() )
6475       aPPs.back().SetAngle( *itAngles++ );
6476   }
6477
6478   TNodeOfNodeListMap   mapNewNodes;
6479   TElemOfVecOfNnlmiMap mapElemNewNodes;
6480   TTElemOfElemListMap  newElemsMap;
6481   TIDSortedElemSet::iterator itElem;
6482   // source elements for each generated one
6483   SMESH_SequenceOfElemPtr srcElems, srcNodes;
6484
6485   // 3. Center of rotation aV0
6486   gp_Pnt aV0 = theRefPoint;
6487   if ( !theHasRefPoint )
6488   {
6489     gp_XYZ aGC( 0.,0.,0. );
6490     TIDSortedElemSet newNodes;
6491
6492     for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6493     {
6494       TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6495       itElem = theElements.begin();
6496       for ( ; itElem != theElements.end(); itElem++ )
6497       {
6498         const SMDS_MeshElement* elem = *itElem;
6499         SMDS_ElemIteratorPtr     itN = elem->nodesIterator();
6500         while ( itN->more() ) {
6501           const SMDS_MeshElement* node = itN->next();
6502           if ( newNodes.insert( node ).second )
6503             aGC += SMESH_TNodeXYZ( node );
6504         }
6505       }
6506     }
6507     aGC /= newNodes.size();
6508     aV0.SetXYZ( aGC );
6509   } // if (!theHasRefPoint) {
6510
6511   // 4. Processing the elements
6512   SMESHDS_Mesh* aMesh = GetMeshDS();
6513   list<const SMDS_MeshNode*> emptyList;
6514
6515   setElemsFirst( theElemSets );
6516   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6517   {
6518     TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6519     for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ )
6520     {
6521       const SMDS_MeshElement* elem = *itElem;
6522
6523       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
6524       newNodesItVec.reserve( elem->NbNodes() );
6525
6526       // loop on elem nodes
6527       int nodeIndex = -1;
6528       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6529       while ( itN->more() )
6530       {
6531         ++nodeIndex;
6532         // check if a node has been already processed
6533         const SMDS_MeshNode* node = cast2Node( itN->next() );
6534         TNodeOfNodeListMap::iterator nIt = mapNewNodes.insert( make_pair( node, emptyList )).first;
6535         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
6536         if ( listNewNodes.empty() )
6537         {
6538           // make new nodes
6539           Standard_Real aAngle1x, aAngleT1T0, aTolAng;
6540           gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
6541           gp_Ax1 anAx1, anAxT1T0;
6542           gp_Dir aDT1x, aDT0x, aDT1T0;
6543
6544           aTolAng=1.e-4;
6545
6546           aV0x = aV0;
6547           aPN0 = SMESH_TNodeXYZ( node );
6548
6549           const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
6550           aP0x = aPP0.Pnt();
6551           aDT0x= aPP0.Tangent();
6552
6553           for ( int j = 1; j < aNbTP; ++j ) {
6554             const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
6555             aP1x     = aPP1.Pnt();
6556             aDT1x    = aPP1.Tangent();
6557             aAngle1x = aPP1.Angle();
6558
6559             gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
6560             // Translation
6561             gp_Vec aV01x( aP0x, aP1x );
6562             aTrsf.SetTranslation( aV01x );
6563
6564             // traslated point
6565             aV1x = aV0x.Transformed( aTrsf );
6566             aPN1 = aPN0.Transformed( aTrsf );
6567
6568             // rotation 1 [ T1,T0 ]
6569             aAngleT1T0=-aDT1x.Angle( aDT0x );
6570             if (fabs(aAngleT1T0) > aTolAng)
6571             {
6572               aDT1T0=aDT1x^aDT0x;
6573               anAxT1T0.SetLocation( aV1x );
6574               anAxT1T0.SetDirection( aDT1T0 );
6575               aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
6576
6577               aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
6578             }
6579
6580             // rotation 2
6581             if ( theHasAngles ) {
6582               anAx1.SetLocation( aV1x );
6583               anAx1.SetDirection( aDT1x );
6584               aTrsfRot.SetRotation( anAx1, aAngle1x );
6585
6586               aPN1 = aPN1.Transformed( aTrsfRot );
6587             }
6588
6589             // make new node
6590             if ( elem->IsQuadratic() && !elem->IsMediumNode(node) )
6591             {
6592               // create additional node
6593               gp_XYZ midP = 0.5 * ( aPN1.XYZ() + aPN0.XYZ() );
6594               const SMDS_MeshNode* newNode = aMesh->AddNode( midP.X(), midP.Y(), midP.Z() );
6595               myLastCreatedNodes.Append(newNode);
6596               srcNodes.Append( node );
6597               listNewNodes.push_back( newNode );
6598             }
6599             const SMDS_MeshNode* newNode = aMesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
6600             myLastCreatedNodes.Append(newNode);
6601             srcNodes.Append( node );
6602             listNewNodes.push_back( newNode );
6603
6604             aPN0 = aPN1;
6605             aP0x = aP1x;
6606             aV0x = aV1x;
6607             aDT0x = aDT1x;
6608           }
6609         }
6610         else if( elem->IsQuadratic() && !elem->IsMediumNode(node) )
6611         {
6612           // if current elem is quadratic and current node is not medium
6613           // we have to check - may be it is needed to insert additional nodes
6614           list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
6615           if ((int) listNewNodes.size() == aNbTP-1 )
6616           {
6617             vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
6618             gp_XYZ P(node->X(), node->Y(), node->Z());
6619             list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
6620             int i;
6621             for(i=0; i<aNbTP-1; i++) {
6622               const SMDS_MeshNode* N = *it;
6623               double x = ( N->X() + P.X() )/2.;
6624               double y = ( N->Y() + P.Y() )/2.;
6625               double z = ( N->Z() + P.Z() )/2.;
6626               const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
6627               srcNodes.Append( node );
6628               myLastCreatedNodes.Append(newN);
6629               aNodes[2*i] = newN;
6630               aNodes[2*i+1] = N;
6631               P = gp_XYZ(N->X(),N->Y(),N->Z());
6632             }
6633             listNewNodes.clear();
6634             for(i=0; i<2*(aNbTP-1); i++) {
6635               listNewNodes.push_back(aNodes[i]);
6636             }
6637           }
6638         }
6639
6640         newNodesItVec.push_back( nIt );
6641       }
6642
6643       // make new elements
6644       sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
6645     }
6646   }
6647
6648   makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], aNbTP-1, srcElems );
6649
6650   if ( theMakeGroups )
6651     generateGroups( srcNodes, srcElems, "extruded");
6652
6653   return EXTR_OK;
6654 }
6655
6656
6657 //=======================================================================
6658 //function : LinearAngleVariation
6659 //purpose  : auxilary for ExtrusionAlongTrack
6660 //=======================================================================
6661 void SMESH_MeshEditor::LinearAngleVariation(const int nbSteps,
6662                                             list<double>& Angles)
6663 {
6664   int nbAngles = Angles.size();
6665   if( nbSteps > nbAngles ) {
6666     vector<double> theAngles(nbAngles);
6667     list<double>::iterator it = Angles.begin();
6668     int i = -1;
6669     for(; it!=Angles.end(); it++) {
6670       i++;
6671       theAngles[i] = (*it);
6672     }
6673     list<double> res;
6674     double rAn2St = double( nbAngles ) / double( nbSteps );
6675     double angPrev = 0, angle;
6676     for ( int iSt = 0; iSt < nbSteps; ++iSt ) {
6677       double angCur = rAn2St * ( iSt+1 );
6678       double angCurFloor  = floor( angCur );
6679       double angPrevFloor = floor( angPrev );
6680       if ( angPrevFloor == angCurFloor )
6681         angle = rAn2St * theAngles[ int( angCurFloor ) ];
6682       else {
6683         int iP = int( angPrevFloor );
6684         double angPrevCeil = ceil(angPrev);
6685         angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6686
6687         int iC = int( angCurFloor );
6688         if ( iC < nbAngles )
6689           angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6690
6691         iP = int( angPrevCeil );
6692         while ( iC-- > iP )
6693           angle += theAngles[ iC ];
6694       }
6695       res.push_back(angle);
6696       angPrev = angCur;
6697     }
6698     Angles.clear();
6699     it = res.begin();
6700     for(; it!=res.end(); it++)
6701       Angles.push_back( *it );
6702   }
6703 }
6704
6705
6706 //================================================================================
6707 /*!
6708  * \brief Move or copy theElements applying theTrsf to their nodes
6709  *  \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6710  *  \param theTrsf - transformation to apply
6711  *  \param theCopy - if true, create translated copies of theElems
6712  *  \param theMakeGroups - if true and theCopy, create translated groups
6713  *  \param theTargetMesh - mesh to copy translated elements into
6714  *  \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6715  */
6716 //================================================================================
6717
6718 SMESH_MeshEditor::PGroupIDs
6719 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6720                              const gp_Trsf&     theTrsf,
6721                              const bool         theCopy,
6722                              const bool         theMakeGroups,
6723                              SMESH_Mesh*        theTargetMesh)
6724 {
6725   myLastCreatedElems.Clear();
6726   myLastCreatedNodes.Clear();
6727
6728   bool needReverse = false;
6729   string groupPostfix;
6730   switch ( theTrsf.Form() ) {
6731   case gp_PntMirror:
6732     needReverse = true;
6733     groupPostfix = "mirrored";
6734     break;
6735   case gp_Ax1Mirror:
6736     groupPostfix = "mirrored";
6737     break;
6738   case gp_Ax2Mirror:
6739     needReverse = true;
6740     groupPostfix = "mirrored";
6741     break;
6742   case gp_Rotation:
6743     groupPostfix = "rotated";
6744     break;
6745   case gp_Translation:
6746     groupPostfix = "translated";
6747     break;
6748   case gp_Scale:
6749     groupPostfix = "scaled";
6750     break;
6751   case gp_CompoundTrsf: // different scale by axis
6752     groupPostfix = "scaled";
6753     break;
6754   default:
6755     needReverse = false;
6756     groupPostfix = "transformed";
6757   }
6758
6759   SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6760   SMESHDS_Mesh* aMesh    = GetMeshDS();
6761
6762   SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6763   SMESH_MeshEditor* editor = theTargetMesh ? & targetMeshEditor : theCopy ? this : 0;
6764   SMESH_MeshEditor::ElemFeatures elemType;
6765
6766   // map old node to new one
6767   TNodeNodeMap nodeMap;
6768
6769   // elements sharing moved nodes; those of them which have all
6770   // nodes mirrored but are not in theElems are to be reversed
6771   TIDSortedElemSet inverseElemSet;
6772
6773   // source elements for each generated one
6774   SMESH_SequenceOfElemPtr srcElems, srcNodes;
6775
6776   // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6777   TIDSortedElemSet orphanNode;
6778
6779   if ( theElems.empty() ) // transform the whole mesh
6780   {
6781     // add all elements
6782     SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6783     while ( eIt->more() ) theElems.insert( eIt->next() );
6784     // add orphan nodes
6785     SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6786     while ( nIt->more() )
6787     {
6788       const SMDS_MeshNode* node = nIt->next();
6789       if ( node->NbInverseElements() == 0)
6790         orphanNode.insert( node );
6791     }
6792   }
6793
6794   // loop on elements to transform nodes : first orphan nodes then elems
6795   TIDSortedElemSet::iterator itElem;
6796   TIDSortedElemSet *elements[] = { &orphanNode, &theElems };
6797   for (int i=0; i<2; i++)
6798     for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ )
6799     {
6800       const SMDS_MeshElement* elem = *itElem;
6801       if ( !elem )
6802         continue;
6803
6804       // loop on elem nodes
6805       double coord[3];
6806       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6807       while ( itN->more() )
6808       {
6809         const SMDS_MeshNode* node = cast2Node( itN->next() );
6810         // check if a node has been already transformed
6811         pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6812           nodeMap.insert( make_pair ( node, node ));
6813         if ( !n2n_isnew.second )
6814           continue;
6815
6816         node->GetXYZ( coord );
6817         theTrsf.Transforms( coord[0], coord[1], coord[2] );
6818         if ( theTargetMesh ) {
6819           const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6820           n2n_isnew.first->second = newNode;
6821           myLastCreatedNodes.Append(newNode);
6822           srcNodes.Append( node );
6823         }
6824         else if ( theCopy ) {
6825           const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6826           n2n_isnew.first->second = newNode;
6827           myLastCreatedNodes.Append(newNode);
6828           srcNodes.Append( node );
6829         }
6830         else {
6831           aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6832           // node position on shape becomes invalid
6833           const_cast< SMDS_MeshNode* > ( node )->SetPosition
6834             ( SMDS_SpacePosition::originSpacePosition() );
6835         }
6836
6837         // keep inverse elements
6838         if ( !theCopy && !theTargetMesh && needReverse ) {
6839           SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6840           while ( invElemIt->more() ) {
6841             const SMDS_MeshElement* iel = invElemIt->next();
6842             inverseElemSet.insert( iel );
6843           }
6844         }
6845       }
6846     } // loop on elems in { &orphanNode, &theElems };
6847
6848   // either create new elements or reverse mirrored ones
6849   if ( !theCopy && !needReverse && !theTargetMesh )
6850     return PGroupIDs();
6851
6852   theElems.insert( inverseElemSet.begin(),inverseElemSet.end() );
6853
6854   // Replicate or reverse elements
6855
6856   std::vector<int> iForw;
6857   vector<const SMDS_MeshNode*> nodes;
6858   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6859   {
6860     const SMDS_MeshElement* elem = *itElem;
6861     if ( !elem ) continue;
6862
6863     SMDSAbs_GeometryType geomType = elem->GetGeomType();
6864     size_t               nbNodes  = elem->NbNodes();
6865     if ( geomType == SMDSGeom_NONE ) continue; // node
6866
6867     nodes.resize( nbNodes );
6868
6869     if ( geomType == SMDSGeom_POLYHEDRA )  // ------------------ polyhedral volume
6870     {
6871       const SMDS_VtkVolume* aPolyedre = dynamic_cast<const SMDS_VtkVolume*>( elem );
6872       if (!aPolyedre)
6873         continue;
6874       nodes.clear();
6875       bool allTransformed = true;
6876       int nbFaces = aPolyedre->NbFaces();
6877       for (int iface = 1; iface <= nbFaces && allTransformed; iface++)
6878       {
6879         int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6880         for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++)
6881         {
6882           const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6883           TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6884           if ( nodeMapIt == nodeMap.end() )
6885             allTransformed = false; // not all nodes transformed
6886           else
6887             nodes.push_back((*nodeMapIt).second);
6888         }
6889         if ( needReverse && allTransformed )
6890           std::reverse( nodes.end() - nbFaceNodes, nodes.end() );
6891       }
6892       if ( !allTransformed )
6893         continue; // not all nodes transformed
6894     }
6895     else // ----------------------- the rest element types
6896     {
6897       while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6898       const vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType(), nbNodes );
6899       const vector<int>&    i = needReverse ? iRev : iForw;
6900
6901       // find transformed nodes
6902       size_t iNode = 0;
6903       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6904       while ( itN->more() ) {
6905         const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
6906         TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6907         if ( nodeMapIt == nodeMap.end() )
6908           break; // not all nodes transformed
6909         nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
6910       }
6911       if ( iNode != nbNodes )
6912         continue; // not all nodes transformed
6913     }
6914
6915     if ( editor ) {
6916       // copy in this or a new mesh
6917       if ( editor->AddElement( nodes, elemType.Init( elem, /*basicOnly=*/false )))
6918         srcElems.Append( elem );
6919     }
6920     else {
6921       // reverse element as it was reversed by transformation
6922       if ( nbNodes > 2 )
6923         aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
6924     }
6925
6926   } // loop on elements
6927
6928   if ( editor && editor != this )
6929     myLastCreatedElems = editor->myLastCreatedElems;
6930
6931   PGroupIDs newGroupIDs;
6932
6933   if ( ( theMakeGroups && theCopy ) ||
6934        ( theMakeGroups && theTargetMesh ) )
6935     newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh, false );
6936
6937   return newGroupIDs;
6938 }
6939
6940 //=======================================================================
6941 /*!
6942  * \brief Create groups of elements made during transformation
6943  *  \param nodeGens - nodes making corresponding myLastCreatedNodes
6944  *  \param elemGens - elements making corresponding myLastCreatedElems
6945  *  \param postfix - to append to names of new groups
6946  *  \param targetMesh - mesh to create groups in
6947  *  \param topPresent - is there "top" elements that are created by sweeping
6948  */
6949 //=======================================================================
6950
6951 SMESH_MeshEditor::PGroupIDs
6952 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
6953                                  const SMESH_SequenceOfElemPtr& elemGens,
6954                                  const std::string&             postfix,
6955                                  SMESH_Mesh*                    targetMesh,
6956                                  const bool                     topPresent)
6957 {
6958   PGroupIDs newGroupIDs( new list<int> );
6959   SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6960
6961   // Sort existing groups by types and collect their names
6962
6963   // containers to store an old group and generated new ones;
6964   // 1st new group is for result elems of different type than a source one;
6965   // 2nd new group is for same type result elems ("top" group at extrusion)
6966   using boost::tuple;
6967   using boost::make_tuple;
6968   typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
6969   vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
6970   vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
6971   // group names
6972   set< string > groupNames;
6973
6974   SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
6975   if ( !groupIt->more() ) return newGroupIDs;
6976
6977   int newGroupID = mesh->GetGroupIds().back()+1;
6978   while ( groupIt->more() )
6979   {
6980     SMESH_Group * group = groupIt->next();
6981     if ( !group ) continue;
6982     SMESHDS_GroupBase* groupDS = group->GetGroupDS();
6983     if ( !groupDS || groupDS->IsEmpty() ) continue;
6984     groupNames.insert    ( group->GetName() );
6985     groupDS->SetStoreName( group->GetName() );
6986     const SMDSAbs_ElementType type = groupDS->GetType();
6987     SMESHDS_Group* newGroup    = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6988     SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6989     groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
6990     orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
6991   }
6992
6993   // Loop on nodes and elements to add them in new groups
6994
6995   vector< const SMDS_MeshElement* > resultElems;
6996   for ( int isNodes = 0; isNodes < 2; ++isNodes )
6997   {
6998     const SMESH_SequenceOfElemPtr& gens  = isNodes ? nodeGens : elemGens;
6999     const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
7000     if ( gens.Length() != elems.Length() )
7001       throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
7002
7003     // loop on created elements
7004     for (int iElem = 1; iElem <= elems.Length(); ++iElem )
7005     {
7006       const SMDS_MeshElement* sourceElem = gens( iElem );
7007       if ( !sourceElem ) {
7008         MESSAGE("generateGroups(): NULL source element");
7009         continue;
7010       }
7011       list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
7012       if ( groupsOldNew.empty() ) { // no groups of this type at all
7013         while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
7014           ++iElem; // skip all elements made by sourceElem
7015         continue;
7016       }
7017       // collect all elements made by the iElem-th sourceElem
7018       resultElems.clear();
7019       if ( const SMDS_MeshElement* resElem = elems( iElem ))
7020         if ( resElem != sourceElem )
7021           resultElems.push_back( resElem );
7022       while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
7023         if ( const SMDS_MeshElement* resElem = elems( ++iElem ))
7024           if ( resElem != sourceElem )
7025             resultElems.push_back( resElem );
7026
7027       const SMDS_MeshElement* topElem = 0;
7028       if ( isNodes ) // there must be a top element
7029       {
7030         topElem = resultElems.back();
7031         resultElems.pop_back();
7032       }
7033       else
7034       {
7035         vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
7036         for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
7037           if ( (*resElemIt)->GetType() == sourceElem->GetType() )
7038           {
7039             topElem = *resElemIt;
7040             *resElemIt = 0; // erase *resElemIt
7041             break;
7042           }
7043       }
7044       // add resultElems to groups originted from ones the sourceElem belongs to
7045       list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
7046       for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
7047       {
7048         SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
7049         if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
7050         {
7051           // fill in a new group
7052           SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
7053           vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
7054           for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
7055             if ( *resElemIt )
7056               newGroup.Add( *resElemIt );
7057
7058           // fill a "top" group
7059           if ( topElem )
7060           {
7061             SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
7062             newTopGroup.Add( topElem );
7063          }
7064         }
7065       }
7066     } // loop on created elements
7067   }// loop on nodes and elements
7068
7069   // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
7070
7071   list<int> topGrouIds;
7072   for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
7073   {
7074     SMESHDS_GroupBase* oldGroupDS =   orderedOldNewGroups[i]->get<0>();
7075     SMESHDS_Group*   newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
7076                                       orderedOldNewGroups[i]->get<2>() };
7077     for ( int is2nd = 0; is2nd < 2; ++is2nd )
7078     {
7079       SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
7080       if ( newGroupDS->IsEmpty() )
7081       {
7082         mesh->GetMeshDS()->RemoveGroup( newGroupDS );
7083       }
7084       else
7085       {
7086         // set group type
7087         newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
7088
7089         // make a name
7090         const bool isTop = ( topPresent &&
7091                              newGroupDS->GetType() == oldGroupDS->GetType() &&
7092                              is2nd );
7093
7094         string name = oldGroupDS->GetStoreName();
7095         { // remove trailing whitespaces (issue 22599)
7096           size_t size = name.size();
7097           while ( size > 1 && isspace( name[ size-1 ]))
7098             --size;
7099           if ( size != name.size() )
7100           {
7101             name.resize( size );
7102             oldGroupDS->SetStoreName( name.c_str() );
7103           }
7104         }
7105         if ( !targetMesh ) {
7106           string suffix = ( isTop ? "top": postfix.c_str() );
7107           name += "_";
7108           name += suffix;
7109           int nb = 1;
7110           while ( !groupNames.insert( name ).second ) // name exists
7111             name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
7112         }
7113         else if ( isTop ) {
7114           name += "_top";
7115         }
7116         newGroupDS->SetStoreName( name.c_str() );
7117
7118         // make a SMESH_Groups
7119         mesh->AddGroup( newGroupDS );
7120         if ( isTop )
7121           topGrouIds.push_back( newGroupDS->GetID() );
7122         else
7123           newGroupIDs->push_back( newGroupDS->GetID() );
7124       }
7125     }
7126   }
7127   newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
7128
7129   return newGroupIDs;
7130 }
7131
7132 //================================================================================
7133 /*!
7134  *  * \brief Return list of group of nodes close to each other within theTolerance
7135  *  *        Search among theNodes or in the whole mesh if theNodes is empty using
7136  *  *        an Octree algorithm
7137  *  \param [in,out] theNodes - the nodes to treat
7138  *  \param [in]     theTolerance - the tolerance
7139  *  \param [out]    theGroupsOfNodes - the result groups of coincident nodes
7140  *  \param [in]     theSeparateCornersAndMedium - if \c true, in quadratic mesh puts 
7141  *         corner and medium nodes in separate groups
7142  */
7143 //================================================================================
7144
7145 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet &   theNodes,
7146                                             const double         theTolerance,
7147                                             TListOfListOfNodes & theGroupsOfNodes,
7148                                             bool                 theSeparateCornersAndMedium)
7149 {
7150   myLastCreatedElems.Clear();
7151   myLastCreatedNodes.Clear();
7152
7153   if ( myMesh->NbEdges  ( ORDER_QUADRATIC ) +
7154        myMesh->NbFaces  ( ORDER_QUADRATIC ) +
7155        myMesh->NbVolumes( ORDER_QUADRATIC ) == 0 )
7156     theSeparateCornersAndMedium = false;
7157
7158   TIDSortedNodeSet& corners = theNodes;
7159   TIDSortedNodeSet  medium;
7160
7161   if ( theNodes.empty() ) // get all nodes in the mesh
7162   {
7163     TIDSortedNodeSet* nodes[2] = { &corners, &medium };
7164     SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(/*idInceasingOrder=*/true);
7165     if ( theSeparateCornersAndMedium )
7166       while ( nIt->more() )
7167       {
7168         const SMDS_MeshNode* n = nIt->next();
7169         TIDSortedNodeSet* & nodeSet = nodes[ SMESH_MesherHelper::IsMedium( n )];
7170         nodeSet->insert( nodeSet->end(), n );
7171       }
7172     else
7173       while ( nIt->more() )
7174         theNodes.insert( theNodes.end(),nIt->next() );
7175   }
7176   else if ( theSeparateCornersAndMedium ) // separate corners from medium nodes
7177   {
7178     TIDSortedNodeSet::iterator nIt = corners.begin();
7179     while ( nIt != corners.end() )
7180       if ( SMESH_MesherHelper::IsMedium( *nIt ))
7181       {
7182         medium.insert( medium.end(), *nIt );
7183         corners.erase( nIt++ );
7184       }
7185       else
7186       {
7187         ++nIt;
7188       }
7189   }
7190
7191   if ( !corners.empty() )
7192     SMESH_OctreeNode::FindCoincidentNodes ( corners, &theGroupsOfNodes, theTolerance );
7193   if ( !medium.empty() )
7194     SMESH_OctreeNode::FindCoincidentNodes ( medium, &theGroupsOfNodes, theTolerance );
7195 }
7196
7197 //=======================================================================
7198 //function : SimplifyFace
7199 //purpose  : split a chain of nodes into several closed chains
7200 //=======================================================================
7201
7202 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
7203                                     vector<const SMDS_MeshNode *>&       poly_nodes,
7204                                     vector<int>&                         quantities) const
7205 {
7206   int nbNodes = faceNodes.size();
7207   while ( faceNodes[ 0 ] == faceNodes[ nbNodes-1 ] && nbNodes > 2 )
7208     --nbNodes;
7209   if ( nbNodes < 3 )
7210     return 0;
7211   size_t prevNbQuant = quantities.size();
7212
7213   vector< const SMDS_MeshNode* > simpleNodes; simpleNodes.reserve( nbNodes );
7214   map< const SMDS_MeshNode*, int > nodeIndices; // indices within simpleNodes
7215   map< const SMDS_MeshNode*, int >::iterator nInd;
7216
7217   nodeIndices.insert( make_pair( faceNodes[0], 0 ));
7218   simpleNodes.push_back( faceNodes[0] );
7219   for ( int iCur = 1; iCur < nbNodes; iCur++ )
7220   {
7221     if ( faceNodes[ iCur ] != simpleNodes.back() )
7222     {
7223       int index = simpleNodes.size();
7224       nInd = nodeIndices.insert( make_pair( faceNodes[ iCur ], index )).first;
7225       int prevIndex = nInd->second;
7226       if ( prevIndex < index )
7227       {
7228         // a sub-loop found
7229         int loopLen = index - prevIndex;
7230         if ( loopLen > 2 )
7231         {
7232           // store the sub-loop
7233           quantities.push_back( loopLen );
7234           for ( int i = prevIndex; i < index; i++ )
7235             poly_nodes.push_back( simpleNodes[ i ]);
7236         }
7237         simpleNodes.resize( prevIndex+1 );
7238       }
7239       else
7240       {
7241         simpleNodes.push_back( faceNodes[ iCur ]);
7242       }
7243     }
7244   }
7245
7246   if ( simpleNodes.size() > 2 )
7247   {
7248     quantities.push_back( simpleNodes.size() );
7249     poly_nodes.insert ( poly_nodes.end(), simpleNodes.begin(), simpleNodes.end() );
7250   }
7251
7252   return quantities.size() - prevNbQuant;
7253 }
7254
7255 //=======================================================================
7256 //function : MergeNodes
7257 //purpose  : In each group, the cdr of nodes are substituted by the first one
7258 //           in all elements.
7259 //=======================================================================
7260
7261 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes)
7262 {
7263   myLastCreatedElems.Clear();
7264   myLastCreatedNodes.Clear();
7265
7266   SMESHDS_Mesh* aMesh = GetMeshDS();
7267
7268   TNodeNodeMap nodeNodeMap; // node to replace - new node
7269   set<const SMDS_MeshElement*> elems; // all elements with changed nodes
7270   list< int > rmElemIds, rmNodeIds;
7271
7272   // Fill nodeNodeMap and elems
7273
7274   TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
7275   for ( ; grIt != theGroupsOfNodes.end(); grIt++ )
7276   {
7277     list<const SMDS_MeshNode*>& nodes = *grIt;
7278     list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
7279     const SMDS_MeshNode* nToKeep = *nIt;
7280     for ( ++nIt; nIt != nodes.end(); nIt++ )
7281     {
7282       const SMDS_MeshNode* nToRemove = *nIt;
7283       nodeNodeMap.insert( make_pair( nToRemove, nToKeep ));
7284       if ( nToRemove != nToKeep )
7285       {
7286         rmNodeIds.push_back( nToRemove->GetID() );
7287         AddToSameGroups( nToKeep, nToRemove, aMesh );
7288         // set _alwaysComputed to a sub-mesh of VERTEX to enable mesh computing
7289         // after MergeNodes() w/o creating node in place of merged ones.
7290         const SMDS_PositionPtr& pos = nToRemove->GetPosition();
7291         if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
7292           if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
7293             sm->SetIsAlwaysComputed( true );
7294       }
7295       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
7296       while ( invElemIt->more() ) {
7297         const SMDS_MeshElement* elem = invElemIt->next();
7298         elems.insert(elem);
7299       }
7300     }
7301   }
7302   // Change element nodes or remove an element
7303
7304   set<const SMDS_MeshNode*> nodeSet;
7305   vector< const SMDS_MeshNode*> curNodes, uniqueNodes;
7306   vector<int> iRepl;
7307   ElemFeatures elemType;
7308
7309   set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7310   for ( ; eIt != elems.end(); eIt++ )
7311   {
7312     const SMDS_MeshElement* elem = *eIt;
7313     const           int  nbNodes = elem->NbNodes();
7314     const           int aShapeId = FindShape( elem );
7315     SMDSAbs_EntityType    entity = elem->GetEntityType();
7316
7317     nodeSet.clear();
7318     curNodes.resize( nbNodes );
7319     uniqueNodes.resize( nbNodes );
7320     iRepl.resize( nbNodes );
7321     int iUnique = 0, iCur = 0, nbRepl = 0;
7322
7323     // get new seq of nodes
7324     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7325     while ( itN->more() )
7326     {
7327       const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7328
7329       TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7330       if ( nnIt != nodeNodeMap.end() ) { // n sticks
7331         n = (*nnIt).second;
7332         { ////////// BUG 0020185: begin
7333           bool stopRecur = false;
7334           set<const SMDS_MeshNode*> nodesRecur;
7335           nodesRecur.insert(n);
7336           while (!stopRecur) {
7337             TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( n );
7338             if ( nnIt_i != nodeNodeMap.end() ) { // n sticks
7339               n = (*nnIt_i).second;
7340               if (!nodesRecur.insert(n).second) {
7341                 // error: recursive dependency
7342                 stopRecur = true;
7343               }
7344             }
7345             else
7346               stopRecur = true;
7347           }
7348         } ////////// BUG 0020185: end
7349       }
7350       curNodes[ iCur ] = n;
7351       bool isUnique = nodeSet.insert( n ).second;
7352       if ( isUnique )
7353         uniqueNodes[ iUnique++ ] = n;
7354       else
7355         iRepl[ nbRepl++ ] = iCur;
7356       iCur++;
7357     }
7358
7359     // Analyse element topology after replacement
7360
7361     bool isOk = true;
7362     int nbUniqueNodes = nodeSet.size();
7363     if ( nbNodes != nbUniqueNodes ) // some nodes stick
7364     {
7365       if ( elem->IsPoly() ) // Polygons and Polyhedral volumes
7366       {
7367         if ( elem->GetType() == SMDSAbs_Face ) // Polygon
7368         {
7369           elemType.Init( elem );
7370           const bool isQuad = elemType.myIsQuad;
7371           if ( isQuad )
7372             SMDS_MeshCell::applyInterlace // interlace medium and corner nodes
7373               ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon, nbNodes ), curNodes );
7374
7375           // a polygon can divide into several elements
7376           vector<const SMDS_MeshNode *> polygons_nodes;
7377           vector<int> quantities;
7378           int nbNew = SimplifyFace( curNodes, polygons_nodes, quantities );
7379           if (nbNew > 0)
7380           {
7381             vector<const SMDS_MeshNode *> face_nodes;
7382             int inode = 0;
7383             for (int iface = 0; iface < nbNew; iface++)
7384             {
7385               int nbNewNodes = quantities[iface];
7386               face_nodes.assign( polygons_nodes.begin() + inode,
7387                                  polygons_nodes.begin() + inode + nbNewNodes );
7388               inode += nbNewNodes;
7389               if ( isQuad ) // check if a result elem is a valid quadratic polygon
7390               {
7391                 bool isValid = ( nbNewNodes % 2 == 0 );
7392                 for ( int i = 0; i < nbNewNodes && isValid; ++i )
7393                   isValid = ( elem->IsMediumNode( face_nodes[i]) == bool( i % 2 ));
7394                 elemType.SetQuad( isValid );
7395                 if ( isValid ) // put medium nodes after corners
7396                   SMDS_MeshCell::applyInterlaceRev
7397                     ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon,
7398                                                           nbNewNodes ), face_nodes );
7399               }
7400               elemType.SetPoly(( nbNewNodes / ( elemType.myIsQuad + 1 ) > 4 ));
7401
7402               SMDS_MeshElement* newElem = AddElement( face_nodes, elemType.SetID(-1));
7403               if ( aShapeId )
7404                 aMesh->SetMeshElementOnShape(newElem, aShapeId);
7405             }
7406           }
7407           rmElemIds.push_back(elem->GetID());
7408
7409         } // Polygon
7410
7411         else if ( elem->GetType() == SMDSAbs_Volume ) // Polyhedral volume
7412         {
7413           if ( nbUniqueNodes < 4 ) {
7414             rmElemIds.push_back(elem->GetID());
7415           }
7416           else {
7417             // each face has to be analyzed in order to check volume validity
7418             const SMDS_VtkVolume* aPolyedre = dynamic_cast<const SMDS_VtkVolume*>( elem );
7419             if ( aPolyedre )
7420             {
7421               int nbFaces = aPolyedre->NbFaces();
7422
7423               vector<const SMDS_MeshNode *> poly_nodes;
7424               vector<int>                   quantities;
7425               vector<const SMDS_MeshNode *> faceNodes;
7426
7427               for (int iface = 1; iface <= nbFaces; iface++)
7428               {
7429                 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7430                 faceNodes.resize( nbFaceNodes );
7431                 for (int inode = 1; inode <= nbFaceNodes; inode++)
7432                 {
7433                   const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7434                   TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7435                   if ( nnIt != nodeNodeMap.end() ) // faceNode sticks
7436                     faceNode = (*nnIt).second;
7437                   faceNodes[inode - 1] = faceNode;
7438                 }
7439                 SimplifyFace(faceNodes, poly_nodes, quantities);
7440               }
7441
7442               if ( quantities.size() > 3 ) {
7443                 // TODO: remove coincident faces
7444               }
7445
7446               if ( quantities.size() > 3 )
7447               {
7448                 const SMDS_MeshElement* newElem =
7449                   aMesh->AddPolyhedralVolume( poly_nodes, quantities );
7450                 myLastCreatedElems.Append( newElem );
7451                 if ( aShapeId && newElem )
7452                   aMesh->SetMeshElementOnShape( newElem, aShapeId );
7453                 rmElemIds.push_back( elem->GetID() );
7454               }
7455             }
7456             else {
7457               rmElemIds.push_back( elem->GetID() );
7458             }
7459           }
7460         }
7461         else {
7462         }
7463
7464         continue;
7465       } // poly element
7466
7467       // Regular elements
7468       // TODO not all the possible cases are solved. Find something more generic?
7469       switch ( entity ) {
7470       case SMDSEntity_Edge: //////// EDGE
7471       case SMDSEntity_Triangle: //// TRIANGLE
7472       case SMDSEntity_Quad_Triangle:
7473       case SMDSEntity_Tetra:
7474       case SMDSEntity_Quad_Tetra: // TETRAHEDRON
7475       {
7476         isOk = false;
7477         break;
7478       }
7479       case SMDSEntity_Quad_Edge:
7480       {
7481         isOk = false; // to linear EDGE ???????
7482         break;
7483       }
7484       case SMDSEntity_Quadrangle: //////////////////////////////////// QUADRANGLE
7485       {
7486         if ( nbUniqueNodes < 3 )
7487           isOk = false;
7488         else if ( nbRepl == 1 && curNodes[ iRepl[0]] == curNodes[( iRepl[0]+2 )%4 ])
7489           isOk = false; // opposite nodes stick
7490         break;
7491       }
7492       case SMDSEntity_Quad_Quadrangle: // Quadratic QUADRANGLE
7493       {
7494         //   1    5    2
7495         //    +---+---+
7496         //    |       |
7497         //   4+       +6
7498         //    |       |
7499         //    +---+---+
7500         //   0    7    3
7501         if (( nbUniqueNodes == 6 && nbRepl == 2 ) &&
7502             (( iRepl[0] == 1 && iRepl[1] == 4 && curNodes[1] == curNodes[0] ) ||
7503              ( iRepl[0] == 2 && iRepl[1] == 5 && curNodes[2] == curNodes[1] ) ||
7504              ( iRepl[0] == 3 && iRepl[1] == 6 && curNodes[3] == curNodes[2] ) ||
7505              ( iRepl[0] == 3 && iRepl[1] == 7 && curNodes[3] == curNodes[0] )))
7506         {
7507           isOk = true;
7508         }
7509         break;
7510       }
7511       case SMDSEntity_BiQuad_Quadrangle: // Bi-Quadratic QUADRANGLE
7512       {
7513         //   1    5    2
7514         //    +---+---+
7515         //    |       |
7516         //   4+  8+   +6
7517         //    |       |
7518         //    +---+---+
7519         //   0    7    3
7520         if (( nbUniqueNodes == 7 && nbRepl == 2 && iRepl[1] != 8 ) &&
7521             (( iRepl[0] == 1 && iRepl[1] == 4 && curNodes[1] == curNodes[0] ) ||
7522              ( iRepl[0] == 2 && iRepl[1] == 5 && curNodes[2] == curNodes[1] ) ||
7523              ( iRepl[0] == 3 && iRepl[1] == 6 && curNodes[3] == curNodes[2] ) ||
7524              ( iRepl[0] == 3 && iRepl[1] == 7 && curNodes[3] == curNodes[0] )))
7525         {
7526           isOk = true;
7527         }
7528         break;
7529       }
7530       case SMDSEntity_Penta: ///////////////////////////////////// PENTAHEDRON
7531       {
7532         isOk = false;
7533         if ( nbUniqueNodes == 4 ) {
7534           // ---------------------------------> tetrahedron
7535           if ( curNodes[3] == curNodes[4] &&
7536                curNodes[3] == curNodes[5] ) {
7537             // top nodes stick
7538             isOk = true;
7539           }
7540           else if ( curNodes[0] == curNodes[1] &&
7541                     curNodes[0] == curNodes[2] ) {
7542             // bottom nodes stick: set a top before
7543             uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7544             uniqueNodes[ 0 ] = curNodes [ 5 ];
7545             uniqueNodes[ 1 ] = curNodes [ 4 ];
7546             uniqueNodes[ 2 ] = curNodes [ 3 ];
7547             isOk = true;
7548           }
7549           else if (( curNodes[0] == curNodes[3] ) +
7550                    ( curNodes[1] == curNodes[4] ) +
7551                    ( curNodes[2] == curNodes[5] ) == 2 ) {
7552             // a lateral face turns into a line
7553             isOk = true;
7554           }
7555         }
7556         else if ( nbUniqueNodes == 5 ) {
7557           // PENTAHEDRON --------------------> pyramid
7558           if ( curNodes[0] == curNodes[3] )
7559           {
7560             uniqueNodes[ 0 ] = curNodes[ 1 ];
7561             uniqueNodes[ 1 ] = curNodes[ 4 ];
7562             uniqueNodes[ 2 ] = curNodes[ 5 ];
7563             uniqueNodes[ 3 ] = curNodes[ 2 ];
7564             uniqueNodes[ 4 ] = curNodes[ 0 ];
7565             isOk = true;
7566           }
7567           if ( curNodes[1] == curNodes[4] )
7568           {
7569             uniqueNodes[ 0 ] = curNodes[ 0 ];
7570             uniqueNodes[ 1 ] = curNodes[ 2 ];
7571             uniqueNodes[ 2 ] = curNodes[ 5 ];
7572             uniqueNodes[ 3 ] = curNodes[ 3 ];
7573             uniqueNodes[ 4 ] = curNodes[ 1 ];
7574             isOk = true;
7575           }
7576           if ( curNodes[2] == curNodes[5] )
7577           {
7578             uniqueNodes[ 0 ] = curNodes[ 0 ];
7579             uniqueNodes[ 1 ] = curNodes[ 3 ];
7580             uniqueNodes[ 2 ] = curNodes[ 4 ];
7581             uniqueNodes[ 3 ] = curNodes[ 1 ];
7582             uniqueNodes[ 4 ] = curNodes[ 2 ];
7583             isOk = true;
7584           }
7585         }
7586         break;
7587       }
7588       case SMDSEntity_Hexa:
7589       {
7590         //////////////////////////////////// HEXAHEDRON
7591         isOk = false;
7592         SMDS_VolumeTool hexa (elem);
7593         hexa.SetExternalNormal();
7594         if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7595           //////////////////////// HEX ---> tetrahedron
7596           for ( int iFace = 0; iFace < 6; iFace++ ) {
7597             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7598             if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7599                 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7600                 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7601               // one face turns into a point ...
7602               int  pickInd = ind[ 0 ];
7603               int iOppFace = hexa.GetOppFaceIndex( iFace );
7604               ind = hexa.GetFaceNodesIndices( iOppFace );
7605               int nbStick = 0;
7606               uniqueNodes.clear();
7607               for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7608                 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7609                   nbStick++;
7610                 else
7611                   uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7612               }
7613               if ( nbStick == 1 ) {
7614                 // ... and the opposite one - into a triangle.
7615                 // set a top node
7616                 uniqueNodes.push_back( curNodes[ pickInd ]);
7617                 isOk = true;
7618               }
7619               break;
7620             }
7621           }
7622         }
7623         else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7624           //////////////////////// HEX ---> prism
7625           int nbTria = 0, iTria[3];
7626           const int *ind; // indices of face nodes
7627           // look for triangular faces
7628           for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7629             ind = hexa.GetFaceNodesIndices( iFace );
7630             TIDSortedNodeSet faceNodes;
7631             for ( iCur = 0; iCur < 4; iCur++ )
7632               faceNodes.insert( curNodes[ind[iCur]] );
7633             if ( faceNodes.size() == 3 )
7634               iTria[ nbTria++ ] = iFace;
7635           }
7636           // check if triangles are opposite
7637           if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7638           {
7639             // set nodes of the bottom triangle
7640             ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7641             vector<int> indB;
7642             for ( iCur = 0; iCur < 4; iCur++ )
7643               if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7644                 indB.push_back( ind[iCur] );
7645             if ( !hexa.IsForward() )
7646               std::swap( indB[0], indB[2] );
7647             for ( iCur = 0; iCur < 3; iCur++ )
7648               uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7649             // set nodes of the top triangle
7650             const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7651             for ( iCur = 0; iCur < 3; ++iCur )
7652               for ( int j = 0; j < 4; ++j )
7653                 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7654                 {
7655                   uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7656                   break;
7657                 }
7658             isOk = true;
7659             break;
7660           }
7661         }
7662         else if (nbUniqueNodes == 5 && nbRepl == 3 ) {
7663           //////////////////// HEXAHEDRON ---> pyramid
7664           for ( int iFace = 0; iFace < 6; iFace++ ) {
7665             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7666             if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7667                 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7668                 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7669               // one face turns into a point ...
7670               int iOppFace = hexa.GetOppFaceIndex( iFace );
7671               ind = hexa.GetFaceNodesIndices( iOppFace );
7672               uniqueNodes.clear();
7673               for ( iCur = 0; iCur < 4; iCur++ ) {
7674                 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7675                   break;
7676                 else
7677                   uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7678               }
7679               if ( uniqueNodes.size() == 4 ) {
7680                 // ... and the opposite one is a quadrangle
7681                 // set a top node
7682                 const int* indTop = hexa.GetFaceNodesIndices( iFace );
7683                 uniqueNodes.push_back( curNodes[indTop[ 0 ]]);
7684                 isOk = true;
7685               }
7686               break;
7687             }
7688           }
7689         }
7690
7691         if ( !isOk && nbUniqueNodes > 4 ) {
7692           ////////////////// HEXAHEDRON ---> polyhedron
7693           hexa.SetExternalNormal();
7694           vector<const SMDS_MeshNode *> poly_nodes; poly_nodes.reserve( 6 * 4 );
7695           vector<int>                   quantities; quantities.reserve( 6 );
7696           for ( int iFace = 0; iFace < 6; iFace++ )
7697           {
7698             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7699             if ( curNodes[ind[0]] == curNodes[ind[2]] ||
7700                  curNodes[ind[1]] == curNodes[ind[3]] )
7701             {
7702               quantities.clear();
7703               break; // opposite nodes stick
7704             }
7705             nodeSet.clear();
7706             for ( iCur = 0; iCur < 4; iCur++ )
7707             {
7708               if ( nodeSet.insert( curNodes[ind[ iCur ]] ).second )
7709                 poly_nodes.push_back( curNodes[ind[ iCur ]]);
7710             }
7711             if ( nodeSet.size() < 3 )
7712               poly_nodes.resize( poly_nodes.size() - nodeSet.size() );
7713             else
7714               quantities.push_back( nodeSet.size() );
7715           }
7716           if ( quantities.size() >= 4 )
7717           {
7718             const SMDS_MeshElement* newElem = aMesh->AddPolyhedralVolume( poly_nodes, quantities );
7719             myLastCreatedElems.Append( newElem );
7720             if ( aShapeId && newElem )
7721               aMesh->SetMeshElementOnShape( newElem, aShapeId );
7722             rmElemIds.push_back( elem->GetID() );
7723           }
7724         }
7725         break;
7726       } // case HEXAHEDRON
7727
7728       default:
7729         isOk = false;
7730       } // switch ( nbNodes )
7731
7732     } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7733
7734     if ( isOk ) // a non-poly elem remains valid after sticking nodes
7735     {
7736       if ( nbNodes != nbUniqueNodes ||
7737            !aMesh->ChangeElementNodes( elem, & curNodes[0], nbNodes ))
7738       {
7739         elemType.Init( elem ).SetID( elem->GetID() );
7740
7741         SMESHDS_SubMesh * sm = aShapeId > 0 ? aMesh->MeshElements(aShapeId) : 0;
7742         aMesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
7743
7744         uniqueNodes.resize(nbUniqueNodes);
7745         SMDS_MeshElement* newElem = this->AddElement( uniqueNodes, elemType );
7746         if ( sm && newElem )
7747           sm->AddElement( newElem );
7748         if ( elem != newElem )
7749           ReplaceElemInGroups( elem, newElem, aMesh );
7750       }
7751     }
7752     else {
7753       // Remove invalid regular element or invalid polygon
7754       rmElemIds.push_back( elem->GetID() );
7755     }
7756
7757   } // loop on elements
7758
7759   // Remove bad elements, then equal nodes (order important)
7760
7761   Remove( rmElemIds, false );
7762   Remove( rmNodeIds, true );
7763
7764   return;
7765 }
7766
7767
7768 // ========================================================
7769 // class   : SortableElement
7770 // purpose : allow sorting elements basing on their nodes
7771 // ========================================================
7772 class SortableElement : public set <const SMDS_MeshElement*>
7773 {
7774 public:
7775
7776   SortableElement( const SMDS_MeshElement* theElem )
7777   {
7778     myElem = theElem;
7779     SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
7780     while ( nodeIt->more() )
7781       this->insert( nodeIt->next() );
7782   }
7783
7784   const SMDS_MeshElement* Get() const
7785   { return myElem; }
7786
7787 private:
7788   mutable const SMDS_MeshElement* myElem;
7789 };
7790
7791 //=======================================================================
7792 //function : FindEqualElements
7793 //purpose  : Return list of group of elements built on the same nodes.
7794 //           Search among theElements or in the whole mesh if theElements is empty
7795 //=======================================================================
7796
7797 void SMESH_MeshEditor::FindEqualElements(TIDSortedElemSet &        theElements,
7798                                          TListOfListOfElementsID & theGroupsOfElementsID)
7799 {
7800   myLastCreatedElems.Clear();
7801   myLastCreatedNodes.Clear();
7802
7803   typedef map< SortableElement, int > TMapOfNodeSet;
7804   typedef list<int> TGroupOfElems;
7805
7806   if ( theElements.empty() )
7807   { // get all elements in the mesh
7808     SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator();
7809     while ( eIt->more() )
7810       theElements.insert( theElements.end(), eIt->next() );
7811   }
7812
7813   vector< TGroupOfElems > arrayOfGroups;
7814   TGroupOfElems groupOfElems;
7815   TMapOfNodeSet mapOfNodeSet;
7816
7817   TIDSortedElemSet::iterator elemIt = theElements.begin();
7818   for ( int i = 0; elemIt != theElements.end(); ++elemIt )
7819   {
7820     const SMDS_MeshElement* curElem = *elemIt;
7821     SortableElement SE(curElem);
7822     // check uniqueness
7823     pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i));
7824     if ( !pp.second ) { // one more coincident elem
7825       TMapOfNodeSet::iterator& itSE = pp.first;
7826       int ind = (*itSE).second;
7827       arrayOfGroups[ind].push_back( curElem->GetID() );
7828     }
7829     else {
7830       arrayOfGroups.push_back( groupOfElems );
7831       arrayOfGroups.back().push_back( curElem->GetID() );
7832       i++;
7833     }
7834   }
7835
7836   groupOfElems.clear();
7837   vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
7838   for ( ; groupIt != arrayOfGroups.end(); ++groupIt )
7839   {
7840     if ( groupIt->size() > 1 ) {
7841       //groupOfElems.sort(); -- theElements is sorted already
7842       theGroupsOfElementsID.push_back( groupOfElems );
7843       theGroupsOfElementsID.back().splice( theGroupsOfElementsID.back().end(), *groupIt );
7844     }
7845   }
7846 }
7847
7848 //=======================================================================
7849 //function : MergeElements
7850 //purpose  : In each given group, substitute all elements by the first one.
7851 //=======================================================================
7852
7853 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
7854 {
7855   myLastCreatedElems.Clear();
7856   myLastCreatedNodes.Clear();
7857
7858   typedef list<int> TListOfIDs;
7859   TListOfIDs rmElemIds; // IDs of elems to remove
7860
7861   SMESHDS_Mesh* aMesh = GetMeshDS();
7862
7863   TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
7864   while ( groupsIt != theGroupsOfElementsID.end() ) {
7865     TListOfIDs& aGroupOfElemID = *groupsIt;
7866     aGroupOfElemID.sort();
7867     int elemIDToKeep = aGroupOfElemID.front();
7868     const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
7869     aGroupOfElemID.pop_front();
7870     TListOfIDs::iterator idIt = aGroupOfElemID.begin();
7871     while ( idIt != aGroupOfElemID.end() ) {
7872       int elemIDToRemove = *idIt;
7873       const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
7874       // add the kept element in groups of removed one (PAL15188)
7875       AddToSameGroups( elemToKeep, elemToRemove, aMesh );
7876       rmElemIds.push_back( elemIDToRemove );
7877       ++idIt;
7878     }
7879     ++groupsIt;
7880   }
7881
7882   Remove( rmElemIds, false );
7883 }
7884
7885 //=======================================================================
7886 //function : MergeEqualElements
7887 //purpose  : Remove all but one of elements built on the same nodes.
7888 //=======================================================================
7889
7890 void SMESH_MeshEditor::MergeEqualElements()
7891 {
7892   TIDSortedElemSet aMeshElements; /* empty input ==
7893                                      to merge equal elements in the whole mesh */
7894   TListOfListOfElementsID aGroupsOfElementsID;
7895   FindEqualElements(aMeshElements, aGroupsOfElementsID);
7896   MergeElements(aGroupsOfElementsID);
7897 }
7898
7899 //=======================================================================
7900 //function : findAdjacentFace
7901 //purpose  :
7902 //=======================================================================
7903
7904 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
7905                                                 const SMDS_MeshNode* n2,
7906                                                 const SMDS_MeshElement* elem)
7907 {
7908   TIDSortedElemSet elemSet, avoidSet;
7909   if ( elem )
7910     avoidSet.insert ( elem );
7911   return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
7912 }
7913
7914 //=======================================================================
7915 //function : findSegment
7916 //purpose  : Return a mesh segment by two nodes one of which can be medium
7917 //=======================================================================
7918
7919 static const SMDS_MeshElement* findSegment(const SMDS_MeshNode* n1,
7920                                            const SMDS_MeshNode* n2)
7921 {
7922   SMDS_ElemIteratorPtr it = n1->GetInverseElementIterator( SMDSAbs_Edge );
7923   while ( it->more() )
7924   {
7925     const SMDS_MeshElement* seg = it->next();
7926     if ( seg->GetNodeIndex( n2 ) >= 0 )
7927       return seg;
7928   }
7929   return 0;
7930 }
7931
7932 //=======================================================================
7933 //function : FindFreeBorder
7934 //purpose  :
7935 //=======================================================================
7936
7937 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
7938
7939 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode*             theFirstNode,
7940                                        const SMDS_MeshNode*             theSecondNode,
7941                                        const SMDS_MeshNode*             theLastNode,
7942                                        list< const SMDS_MeshNode* > &   theNodes,
7943                                        list< const SMDS_MeshElement* >& theFaces)
7944 {
7945   if ( !theFirstNode || !theSecondNode )
7946     return false;
7947   // find border face between theFirstNode and theSecondNode
7948   const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
7949   if ( !curElem )
7950     return false;
7951
7952   theFaces.push_back( curElem );
7953   theNodes.push_back( theFirstNode );
7954   theNodes.push_back( theSecondNode );
7955
7956   const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
7957   TIDSortedElemSet foundElems;
7958   bool needTheLast = ( theLastNode != 0 );
7959
7960   while ( nStart != theLastNode ) {
7961     if ( nStart == theFirstNode )
7962       return !needTheLast;
7963
7964     // find all free border faces sharing form nStart
7965
7966     list< const SMDS_MeshElement* > curElemList;
7967     list< const SMDS_MeshNode* >    nStartList;
7968     SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
7969     while ( invElemIt->more() ) {
7970       const SMDS_MeshElement* e = invElemIt->next();
7971       if ( e == curElem || foundElems.insert( e ).second ) {
7972         // get nodes
7973         int iNode = 0, nbNodes = e->NbNodes();
7974         vector<const SMDS_MeshNode*> nodes(nbNodes+1);
7975
7976         if ( e->IsQuadratic() ) {
7977           const SMDS_VtkFace* F =
7978             dynamic_cast<const SMDS_VtkFace*>(e);
7979           if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
7980           // use special nodes iterator
7981           SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
7982           while( anIter->more() ) {
7983             nodes[ iNode++ ] = cast2Node(anIter->next());
7984           }
7985         }
7986         else {
7987           SMDS_ElemIteratorPtr nIt = e->nodesIterator();
7988           while ( nIt->more() )
7989             nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
7990         }
7991         nodes[ iNode ] = nodes[ 0 ];
7992         // check 2 links
7993         for ( iNode = 0; iNode < nbNodes; iNode++ )
7994           if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
7995                (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
7996               ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
7997           {
7998             nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
7999             curElemList.push_back( e );
8000           }
8001       }
8002     }
8003     // analyse the found
8004
8005     int nbNewBorders = curElemList.size();
8006     if ( nbNewBorders == 0 ) {
8007       // no free border furthermore
8008       return !needTheLast;
8009     }
8010     else if ( nbNewBorders == 1 ) {
8011       // one more element found
8012       nIgnore = nStart;
8013       nStart = nStartList.front();
8014       curElem = curElemList.front();
8015       theFaces.push_back( curElem );
8016       theNodes.push_back( nStart );
8017     }
8018     else {
8019       // several continuations found
8020       list< const SMDS_MeshElement* >::iterator curElemIt;
8021       list< const SMDS_MeshNode* >::iterator nStartIt;
8022       // check if one of them reached the last node
8023       if ( needTheLast ) {
8024         for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8025              curElemIt!= curElemList.end();
8026              curElemIt++, nStartIt++ )
8027           if ( *nStartIt == theLastNode ) {
8028             theFaces.push_back( *curElemIt );
8029             theNodes.push_back( *nStartIt );
8030             return true;
8031           }
8032       }
8033       // find the best free border by the continuations
8034       list<const SMDS_MeshNode*>    contNodes[ 2 ], *cNL;
8035       list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
8036       for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8037            curElemIt!= curElemList.end();
8038            curElemIt++, nStartIt++ )
8039       {
8040         cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
8041         cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
8042         // find one more free border
8043         if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
8044           cNL->clear();
8045           cFL->clear();
8046         }
8047         else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
8048           // choice: clear a worse one
8049           int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
8050           int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
8051           contNodes[ iWorse ].clear();
8052           contFaces[ iWorse ].clear();
8053         }
8054       }
8055       if ( contNodes[0].empty() && contNodes[1].empty() )
8056         return false;
8057
8058       // append the best free border
8059       cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
8060       cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8061       theNodes.pop_back(); // remove nIgnore
8062       theNodes.pop_back(); // remove nStart
8063       theFaces.pop_back(); // remove curElem
8064       list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
8065       list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
8066       for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
8067       for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
8068       return true;
8069
8070     } // several continuations found
8071   } // while ( nStart != theLastNode )
8072
8073   return true;
8074 }
8075
8076 //=======================================================================
8077 //function : CheckFreeBorderNodes
8078 //purpose  : Return true if the tree nodes are on a free border
8079 //=======================================================================
8080
8081 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8082                                             const SMDS_MeshNode* theNode2,
8083                                             const SMDS_MeshNode* theNode3)
8084 {
8085   list< const SMDS_MeshNode* > nodes;
8086   list< const SMDS_MeshElement* > faces;
8087   return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8088 }
8089
8090 //=======================================================================
8091 //function : SewFreeBorder
8092 //purpose  :
8093 //warning  : for border-to-side sewing theSideSecondNode is considered as
8094 //           the last side node and theSideThirdNode is not used
8095 //=======================================================================
8096
8097 SMESH_MeshEditor::Sew_Error
8098 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8099                                  const SMDS_MeshNode* theBordSecondNode,
8100                                  const SMDS_MeshNode* theBordLastNode,
8101                                  const SMDS_MeshNode* theSideFirstNode,
8102                                  const SMDS_MeshNode* theSideSecondNode,
8103                                  const SMDS_MeshNode* theSideThirdNode,
8104                                  const bool           theSideIsFreeBorder,
8105                                  const bool           toCreatePolygons,
8106                                  const bool           toCreatePolyedrs)
8107 {
8108   myLastCreatedElems.Clear();
8109   myLastCreatedNodes.Clear();
8110
8111   Sew_Error aResult = SEW_OK;
8112
8113   // ====================================
8114   //    find side nodes and elements
8115   // ====================================
8116
8117   list< const SMDS_MeshNode* >    nSide[ 2 ];
8118   list< const SMDS_MeshElement* > eSide[ 2 ];
8119   list< const SMDS_MeshNode* >::iterator    nIt[ 2 ];
8120   list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8121
8122   // Free border 1
8123   // --------------
8124   if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8125                       nSide[0], eSide[0])) {
8126     MESSAGE(" Free Border 1 not found " );
8127     aResult = SEW_BORDER1_NOT_FOUND;
8128   }
8129   if (theSideIsFreeBorder) {
8130     // Free border 2
8131     // --------------
8132     if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8133                         nSide[1], eSide[1])) {
8134       MESSAGE(" Free Border 2 not found " );
8135       aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8136     }
8137   }
8138   if ( aResult != SEW_OK )
8139     return aResult;
8140
8141   if (!theSideIsFreeBorder) {
8142     // Side 2
8143     // --------------
8144
8145     // -------------------------------------------------------------------------
8146     // Algo:
8147     // 1. If nodes to merge are not coincident, move nodes of the free border
8148     //    from the coord sys defined by the direction from the first to last
8149     //    nodes of the border to the correspondent sys of the side 2
8150     // 2. On the side 2, find the links most co-directed with the correspondent
8151     //    links of the free border
8152     // -------------------------------------------------------------------------
8153
8154     // 1. Since sewing may break if there are volumes to split on the side 2,
8155     //    we wont move nodes but just compute new coordinates for them
8156     typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8157     TNodeXYZMap nBordXYZ;
8158     list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8159     list< const SMDS_MeshNode* >::iterator nBordIt;
8160
8161     gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8162     gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8163     gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8164     gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8165     double tol2 = 1.e-8;
8166     gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8167     if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8168       // Need node movement.
8169
8170       // find X and Z axes to create trsf
8171       gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8172       gp_Vec X = Zs ^ Zb;
8173       if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8174         // Zb || Zs
8175         X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8176
8177       // coord systems
8178       gp_Ax3 toBordAx( Pb1, Zb, X );
8179       gp_Ax3 fromSideAx( Ps1, Zs, X );
8180       gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8181       // set trsf
8182       gp_Trsf toBordSys, fromSide2Sys;
8183       toBordSys.SetTransformation( toBordAx );
8184       fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8185       fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8186
8187       // move
8188       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8189         const SMDS_MeshNode* n = *nBordIt;
8190         gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8191         toBordSys.Transforms( xyz );
8192         fromSide2Sys.Transforms( xyz );
8193         nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8194       }
8195     }
8196     else {
8197       // just insert nodes XYZ in the nBordXYZ map
8198       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8199         const SMDS_MeshNode* n = *nBordIt;
8200         nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8201       }
8202     }
8203
8204     // 2. On the side 2, find the links most co-directed with the correspondent
8205     //    links of the free border
8206
8207     list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8208     list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8209     sideNodes.push_back( theSideFirstNode );
8210
8211     bool hasVolumes = false;
8212     LinkID_Gen aLinkID_Gen( GetMeshDS() );
8213     set<long> foundSideLinkIDs, checkedLinkIDs;
8214     SMDS_VolumeTool volume;
8215     //const SMDS_MeshNode* faceNodes[ 4 ];
8216
8217     const SMDS_MeshNode*    sideNode;
8218     const SMDS_MeshElement* sideElem  = 0;
8219     const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8220     const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8221     nBordIt = bordNodes.begin();
8222     nBordIt++;
8223     // border node position and border link direction to compare with
8224     gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8225     gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8226     // choose next side node by link direction or by closeness to
8227     // the current border node:
8228     bool searchByDir = ( *nBordIt != theBordLastNode );
8229     do {
8230       // find the next node on the Side 2
8231       sideNode = 0;
8232       double maxDot = -DBL_MAX, minDist = DBL_MAX;
8233       long linkID;
8234       checkedLinkIDs.clear();
8235       gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8236
8237       // loop on inverse elements of current node (prevSideNode) on the Side 2
8238       SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8239       while ( invElemIt->more() )
8240       {
8241         const SMDS_MeshElement* elem = invElemIt->next();
8242         // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8243         int iPrevNode = 0, iNode = 0, nbNodes = elem->NbNodes();
8244         vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8245         bool isVolume = volume.Set( elem );
8246         const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8247         if ( isVolume ) // --volume
8248           hasVolumes = true;
8249         else if ( elem->GetType()==SMDSAbs_Face ) { // --face
8250           // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8251           if(elem->IsQuadratic()) {
8252             const SMDS_VtkFace* F =
8253               dynamic_cast<const SMDS_VtkFace*>(elem);
8254             if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8255             // use special nodes iterator
8256             SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8257             while( anIter->more() ) {
8258               nodes[ iNode ] = cast2Node(anIter->next());
8259               if ( nodes[ iNode++ ] == prevSideNode )
8260                 iPrevNode = iNode - 1;
8261             }
8262           }
8263           else {
8264             SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
8265             while ( nIt->more() ) {
8266               nodes[ iNode ] = cast2Node( nIt->next() );
8267               if ( nodes[ iNode++ ] == prevSideNode )
8268                 iPrevNode = iNode - 1;
8269             }
8270           }
8271           // there are 2 links to check
8272           nbNodes = 2;
8273         }
8274         else // --edge
8275           continue;
8276         // loop on links, to be precise, on the second node of links
8277         for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8278           const SMDS_MeshNode* n = nodes[ iNode ];
8279           if ( isVolume ) {
8280             if ( !volume.IsLinked( n, prevSideNode ))
8281               continue;
8282           }
8283           else {
8284             if ( iNode ) // a node before prevSideNode
8285               n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8286             else         // a node after prevSideNode
8287               n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8288           }
8289           // check if this link was already used
8290           long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8291           bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8292           if (!isJustChecked &&
8293               foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8294           {
8295             // test a link geometrically
8296             gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8297             bool linkIsBetter = false;
8298             double dot = 0.0, dist = 0.0;
8299             if ( searchByDir ) { // choose most co-directed link
8300               dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8301               linkIsBetter = ( dot > maxDot );
8302             }
8303             else { // choose link with the node closest to bordPos
8304               dist = ( nextXYZ - bordPos ).SquareModulus();
8305               linkIsBetter = ( dist < minDist );
8306             }
8307             if ( linkIsBetter ) {
8308               maxDot = dot;
8309               minDist = dist;
8310               linkID = iLink;
8311               sideNode = n;
8312               sideElem = elem;
8313             }
8314           }
8315         }
8316       } // loop on inverse elements of prevSideNode
8317
8318       if ( !sideNode ) {
8319         MESSAGE(" Cant find path by links of the Side 2 ");
8320         return SEW_BAD_SIDE_NODES;
8321       }
8322       sideNodes.push_back( sideNode );
8323       sideElems.push_back( sideElem );
8324       foundSideLinkIDs.insert ( linkID );
8325       prevSideNode = sideNode;
8326
8327       if ( *nBordIt == theBordLastNode )
8328         searchByDir = false;
8329       else {
8330         // find the next border link to compare with
8331         gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8332         searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8333         // move to next border node if sideNode is before forward border node (bordPos)
8334         while ( *nBordIt != theBordLastNode && !searchByDir ) {
8335           prevBordNode = *nBordIt;
8336           nBordIt++;
8337           bordPos = nBordXYZ[ *nBordIt ];
8338           bordDir = bordPos - nBordXYZ[ prevBordNode ];
8339           searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8340         }
8341       }
8342     }
8343     while ( sideNode != theSideSecondNode );
8344
8345     if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8346       MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8347       return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8348     }
8349   } // end nodes search on the side 2
8350
8351   // ============================
8352   // sew the border to the side 2
8353   // ============================
8354
8355   int nbNodes[]  = { (int)nSide[0].size(), (int)nSide[1].size() };
8356   int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8357
8358   bool toMergeConformal = ( nbNodes[0] == nbNodes[1] );
8359   if ( toMergeConformal && toCreatePolygons )
8360   {
8361     // do not merge quadrangles if polygons are OK (IPAL0052824)
8362     eIt[0] = eSide[0].begin();
8363     eIt[1] = eSide[1].begin();
8364     bool allQuads[2] = { true, true };
8365     for ( int iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8366       for ( ; allQuads[iBord] && eIt[iBord] != eSide[iBord].end(); ++eIt[iBord] )
8367         allQuads[iBord] = ( (*eIt[iBord])->NbCornerNodes() == 4 );
8368     }
8369     toMergeConformal = ( !allQuads[0] && !allQuads[1] );
8370   }
8371
8372   TListOfListOfNodes nodeGroupsToMerge;
8373   if (( toMergeConformal ) ||
8374       ( theSideIsFreeBorder && !theSideThirdNode )) {
8375
8376     // all nodes are to be merged
8377
8378     for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8379          nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8380          nIt[0]++, nIt[1]++ )
8381     {
8382       nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8383       nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8384       nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8385     }
8386   }
8387   else {
8388
8389     // insert new nodes into the border and the side to get equal nb of segments
8390
8391     // get normalized parameters of nodes on the borders
8392     vector< double > param[ 2 ];
8393     param[0].resize( maxNbNodes );
8394     param[1].resize( maxNbNodes );
8395     int iNode, iBord;
8396     for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8397       list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8398       list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8399       const SMDS_MeshNode* nPrev = *nIt;
8400       double bordLength = 0;
8401       for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8402         const SMDS_MeshNode* nCur = *nIt;
8403         gp_XYZ segment (nCur->X() - nPrev->X(),
8404                         nCur->Y() - nPrev->Y(),
8405                         nCur->Z() - nPrev->Z());
8406         double segmentLen = segment.Modulus();
8407         bordLength += segmentLen;
8408         param[ iBord ][ iNode ] = bordLength;
8409         nPrev = nCur;
8410       }
8411       // normalize within [0,1]
8412       for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8413         param[ iBord ][ iNode ] /= bordLength;
8414       }
8415     }
8416
8417     // loop on border segments
8418     const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8419     int i[ 2 ] = { 0, 0 };
8420     nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8421     nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8422
8423     TElemOfNodeListMap insertMap;
8424     TElemOfNodeListMap::iterator insertMapIt;
8425     // insertMap is
8426     // key:   elem to insert nodes into
8427     // value: 2 nodes to insert between + nodes to be inserted
8428     do {
8429       bool next[ 2 ] = { false, false };
8430
8431       // find min adjacent segment length after sewing
8432       double nextParam = 10., prevParam = 0;
8433       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8434         if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8435           nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8436         if ( i[ iBord ] > 0 )
8437           prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8438       }
8439       double  minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8440       double  maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8441       double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8442
8443       // choose to insert or to merge nodes
8444       double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8445       if ( Abs( du ) <= minSegLen * 0.2 ) {
8446         // merge
8447         // ------
8448         nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8449         const SMDS_MeshNode* n0 = *nIt[0];
8450         const SMDS_MeshNode* n1 = *nIt[1];
8451         nodeGroupsToMerge.back().push_back( n1 );
8452         nodeGroupsToMerge.back().push_back( n0 );
8453         // position of node of the border changes due to merge
8454         param[ 0 ][ i[0] ] += du;
8455         // move n1 for the sake of elem shape evaluation during insertion.
8456         // n1 will be removed by MergeNodes() anyway
8457         const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8458         next[0] = next[1] = true;
8459       }
8460       else {
8461         // insert
8462         // ------
8463         int intoBord = ( du < 0 ) ? 0 : 1;
8464         const SMDS_MeshElement* elem = *eIt [ intoBord ];
8465         const SMDS_MeshNode*    n1   = nPrev[ intoBord ];
8466         const SMDS_MeshNode*    n2   = *nIt [ intoBord ];
8467         const SMDS_MeshNode*    nIns = *nIt [ 1 - intoBord ];
8468         if ( intoBord == 1 ) {
8469           // move node of the border to be on a link of elem of the side
8470           gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8471           gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8472           double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8473           gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8474           GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8475         }
8476         insertMapIt = insertMap.find( elem );
8477         bool  notFound = ( insertMapIt == insertMap.end() );
8478         bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8479         if ( otherLink ) {
8480           // insert into another link of the same element:
8481           // 1. perform insertion into the other link of the elem
8482           list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8483           const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8484           const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8485           InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8486           // 2. perform insertion into the link of adjacent faces
8487           while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem )) {
8488             InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8489           }
8490           while ( const SMDS_MeshElement* seg = findSegment( n12, n22 )) {
8491             InsertNodesIntoLink( seg, n12, n22, nodeList );
8492           }
8493           if (toCreatePolyedrs) {
8494             // perform insertion into the links of adjacent volumes
8495             UpdateVolumes(n12, n22, nodeList);
8496           }
8497           // 3. find an element appeared on n1 and n2 after the insertion
8498           insertMap.erase( elem );
8499           elem = findAdjacentFace( n1, n2, 0 );
8500         }
8501         if ( notFound || otherLink ) {
8502           // add element and nodes of the side into the insertMap
8503           insertMapIt = insertMap.insert( make_pair( elem, list<const SMDS_MeshNode*>() )).first;
8504           (*insertMapIt).second.push_back( n1 );
8505           (*insertMapIt).second.push_back( n2 );
8506         }
8507         // add node to be inserted into elem
8508         (*insertMapIt).second.push_back( nIns );
8509         next[ 1 - intoBord ] = true;
8510       }
8511
8512       // go to the next segment
8513       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8514         if ( next[ iBord ] ) {
8515           if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8516             eIt[ iBord ]++;
8517           nPrev[ iBord ] = *nIt[ iBord ];
8518           nIt[ iBord ]++; i[ iBord ]++;
8519         }
8520       }
8521     }
8522     while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8523
8524     // perform insertion of nodes into elements
8525
8526     for (insertMapIt = insertMap.begin();
8527          insertMapIt != insertMap.end();
8528          insertMapIt++ )
8529     {
8530       const SMDS_MeshElement* elem = (*insertMapIt).first;
8531       list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8532       const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8533       const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8534
8535       InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8536
8537       while ( const SMDS_MeshElement* seg = findSegment( n1, n2 )) {
8538         InsertNodesIntoLink( seg, n1, n2, nodeList );
8539       }
8540
8541       if ( !theSideIsFreeBorder ) {
8542         // look for and insert nodes into the faces adjacent to elem
8543         while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem )) {
8544           InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8545         }
8546       }
8547       if (toCreatePolyedrs) {
8548         // perform insertion into the links of adjacent volumes
8549         UpdateVolumes(n1, n2, nodeList);
8550       }
8551     }
8552   } // end: insert new nodes
8553
8554   MergeNodes ( nodeGroupsToMerge );
8555
8556
8557   // Remove coincident segments
8558
8559   // get new segments
8560   TIDSortedElemSet segments;
8561   SMESH_SequenceOfElemPtr newFaces;
8562   for ( int i = 1; i <= myLastCreatedElems.Length(); ++i )
8563   {
8564     if ( !myLastCreatedElems(i) ) continue;
8565     if ( myLastCreatedElems(i)->GetType() == SMDSAbs_Edge )
8566       segments.insert( segments.end(), myLastCreatedElems(i) );
8567     else
8568       newFaces.Append( myLastCreatedElems(i) );
8569   }
8570   // get segments adjacent to merged nodes
8571   TListOfListOfNodes::iterator groupIt = nodeGroupsToMerge.begin();
8572   for ( ; groupIt != nodeGroupsToMerge.end(); groupIt++ )
8573   {
8574     const list<const SMDS_MeshNode*>& nodes = *groupIt;
8575     SMDS_ElemIteratorPtr segIt = nodes.front()->GetInverseElementIterator( SMDSAbs_Edge );
8576     while ( segIt->more() )
8577       segments.insert( segIt->next() );
8578   }
8579
8580   // find coincident
8581   TListOfListOfElementsID equalGroups;
8582   if ( !segments.empty() )
8583     FindEqualElements( segments, equalGroups );
8584   if ( !equalGroups.empty() )
8585   {
8586     // remove from segments those that will be removed
8587     TListOfListOfElementsID::iterator itGroups = equalGroups.begin();
8588     for ( ; itGroups != equalGroups.end(); ++itGroups )
8589     {
8590       list< int >& group = *itGroups;
8591       list< int >::iterator id = group.begin();
8592       for ( ++id; id != group.end(); ++id )
8593         if ( const SMDS_MeshElement* seg = GetMeshDS()->FindElement( *id ))
8594           segments.erase( seg );
8595     }
8596     // remove equal segments
8597     MergeElements( equalGroups );
8598
8599     // restore myLastCreatedElems
8600     myLastCreatedElems = newFaces;
8601     TIDSortedElemSet::iterator seg = segments.begin();
8602     for ( ; seg != segments.end(); ++seg )
8603       myLastCreatedElems.Append( *seg );
8604   }
8605
8606   return aResult;
8607 }
8608
8609 //=======================================================================
8610 //function : InsertNodesIntoLink
8611 //purpose  : insert theNodesToInsert into theElement between theBetweenNode1
8612 //           and theBetweenNode2 and split theElement
8613 //=======================================================================
8614
8615 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement*     theElement,
8616                                            const SMDS_MeshNode*        theBetweenNode1,
8617                                            const SMDS_MeshNode*        theBetweenNode2,
8618                                            list<const SMDS_MeshNode*>& theNodesToInsert,
8619                                            const bool                  toCreatePoly)
8620 {
8621   if ( !theElement ) return;
8622
8623   SMESHDS_Mesh *aMesh = GetMeshDS();
8624   vector<const SMDS_MeshElement*> newElems;
8625
8626   if ( theElement->GetType() == SMDSAbs_Edge )
8627   {
8628     theNodesToInsert.push_front( theBetweenNode1 );
8629     theNodesToInsert.push_back ( theBetweenNode2 );
8630     list<const SMDS_MeshNode*>::iterator n = theNodesToInsert.begin();
8631     const SMDS_MeshNode* n1 = *n;
8632     for ( ++n; n != theNodesToInsert.end(); ++n )
8633     {
8634       const SMDS_MeshNode* n2 = *n;
8635       if ( const SMDS_MeshElement* seg = aMesh->FindEdge( n1, n2 ))
8636         AddToSameGroups( seg, theElement, aMesh );
8637       else
8638         newElems.push_back( aMesh->AddEdge ( n1, n2 ));
8639       n1 = n2;
8640     }
8641     theNodesToInsert.pop_front();
8642     theNodesToInsert.pop_back();
8643
8644     if ( theElement->IsQuadratic() ) // add a not split part
8645     {
8646       vector<const SMDS_MeshNode*> nodes( theElement->begin_nodes(),
8647                                           theElement->end_nodes() );
8648       int iOther = 0, nbN = nodes.size();
8649       for ( ; iOther < nbN; ++iOther )
8650         if ( nodes[iOther] != theBetweenNode1 &&
8651              nodes[iOther] != theBetweenNode2 )
8652           break;
8653       if      ( iOther == 0 )
8654       {
8655         if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[0], nodes[1] ))
8656           AddToSameGroups( seg, theElement, aMesh );
8657         else
8658           newElems.push_back( aMesh->AddEdge ( nodes[0], nodes[1] ));
8659       }
8660       else if ( iOther == 2 )
8661       {
8662         if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[1], nodes[2] ))
8663           AddToSameGroups( seg, theElement, aMesh );
8664         else
8665           newElems.push_back( aMesh->AddEdge ( nodes[1], nodes[2] ));
8666       }
8667     }
8668     // treat new elements
8669     for ( size_t i = 0; i < newElems.size(); ++i )
8670       if ( newElems[i] )
8671       {
8672         aMesh->SetMeshElementOnShape( newElems[i], theElement->getshapeId() );
8673         myLastCreatedElems.Append( newElems[i] );
8674       }
8675     ReplaceElemInGroups( theElement, newElems, aMesh );
8676     aMesh->RemoveElement( theElement );
8677     return;
8678
8679   } // if ( theElement->GetType() == SMDSAbs_Edge )
8680
8681   const SMDS_MeshElement* theFace = theElement;
8682   if ( theFace->GetType() != SMDSAbs_Face ) return;
8683
8684   // find indices of 2 link nodes and of the rest nodes
8685   int iNode = 0, il1, il2, i3, i4;
8686   il1 = il2 = i3 = i4 = -1;
8687   vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8688
8689   SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8690   while ( nodeIt->more() ) {
8691     const SMDS_MeshNode* n = nodeIt->next();
8692     if ( n == theBetweenNode1 )
8693       il1 = iNode;
8694     else if ( n == theBetweenNode2 )
8695       il2 = iNode;
8696     else if ( i3 < 0 )
8697       i3 = iNode;
8698     else
8699       i4 = iNode;
8700     nodes[ iNode++ ] = n;
8701   }
8702   if ( il1 < 0 || il2 < 0 || i3 < 0 )
8703     return ;
8704
8705   // arrange link nodes to go one after another regarding the face orientation
8706   bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8707   list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8708   if ( reverse ) {
8709     iNode = il1;
8710     il1 = il2;
8711     il2 = iNode;
8712     aNodesToInsert.reverse();
8713   }
8714   // check that not link nodes of a quadrangles are in good order
8715   int nbFaceNodes = theFace->NbNodes();
8716   if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8717     iNode = i3;
8718     i3 = i4;
8719     i4 = iNode;
8720   }
8721
8722   if (toCreatePoly || theFace->IsPoly()) {
8723
8724     iNode = 0;
8725     vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8726
8727     // add nodes of face up to first node of link
8728     bool isFLN = false;
8729
8730     if ( theFace->IsQuadratic() ) {
8731       const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>(theFace);
8732       if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8733       // use special nodes iterator
8734       SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8735       while( anIter->more()  && !isFLN ) {
8736         const SMDS_MeshNode* n = cast2Node(anIter->next());
8737         poly_nodes[iNode++] = n;
8738         if (n == nodes[il1]) {
8739           isFLN = true;
8740         }
8741       }
8742       // add nodes to insert
8743       list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8744       for (; nIt != aNodesToInsert.end(); nIt++) {
8745         poly_nodes[iNode++] = *nIt;
8746       }
8747       // add nodes of face starting from last node of link
8748       while ( anIter->more() ) {
8749         poly_nodes[iNode++] = cast2Node(anIter->next());
8750       }
8751     }
8752     else {
8753       SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8754       while ( nodeIt->more() && !isFLN ) {
8755         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8756         poly_nodes[iNode++] = n;
8757         if (n == nodes[il1]) {
8758           isFLN = true;
8759         }
8760       }
8761       // add nodes to insert
8762       list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8763       for (; nIt != aNodesToInsert.end(); nIt++) {
8764         poly_nodes[iNode++] = *nIt;
8765       }
8766       // add nodes of face starting from last node of link
8767       while ( nodeIt->more() ) {
8768         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8769         poly_nodes[iNode++] = n;
8770       }
8771     }
8772
8773     // make a new face
8774     newElems.push_back( aMesh->AddPolygonalFace( poly_nodes ));
8775   }
8776
8777   else if ( !theFace->IsQuadratic() )
8778   {
8779     // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8780     int nbLinkNodes = 2 + aNodesToInsert.size();
8781     //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8782     vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8783     linkNodes[ 0 ] = nodes[ il1 ];
8784     linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8785     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8786     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8787       linkNodes[ iNode++ ] = *nIt;
8788     }
8789     // decide how to split a quadrangle: compare possible variants
8790     // and choose which of splits to be a quadrangle
8791     int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad = 0;
8792     if ( nbFaceNodes == 3 ) {
8793       iBestQuad = nbSplits;
8794       i4 = i3;
8795     }
8796     else if ( nbFaceNodes == 4 ) {
8797       SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8798       double aBestRate = DBL_MAX;
8799       for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8800         i1 = 0; i2 = 1;
8801         double aBadRate = 0;
8802         // evaluate elements quality
8803         for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8804           if ( iSplit == iQuad ) {
8805             SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8806                                    linkNodes[ i2++ ],
8807                                    nodes[ i3 ],
8808                                    nodes[ i4 ]);
8809             aBadRate += getBadRate( &quad, aCrit );
8810           }
8811           else {
8812             SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8813                                    linkNodes[ i2++ ],
8814                                    nodes[ iSplit < iQuad ? i4 : i3 ]);
8815             aBadRate += getBadRate( &tria, aCrit );
8816           }
8817         }
8818         // choice
8819         if ( aBadRate < aBestRate ) {
8820           iBestQuad = iQuad;
8821           aBestRate = aBadRate;
8822         }
8823       }
8824     }
8825
8826     // create new elements
8827     i1 = 0; i2 = 1;
8828     for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ )
8829     {
8830       if ( iSplit == iBestQuad )
8831         newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8832                                             linkNodes[ i2++ ],
8833                                             nodes[ i3 ],
8834                                             nodes[ i4 ]));
8835       else
8836         newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8837                                             linkNodes[ i2++ ],
8838                                             nodes[ iSplit < iBestQuad ? i4 : i3 ]));
8839     }
8840
8841     const SMDS_MeshNode* newNodes[ 4 ];
8842     newNodes[ 0 ] = linkNodes[ i1 ];
8843     newNodes[ 1 ] = linkNodes[ i2 ];
8844     newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
8845     newNodes[ 3 ] = nodes[ i4 ];
8846     if (iSplit == iBestQuad)
8847       newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] ));
8848     else
8849       newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] ));
8850
8851   } // end if(!theFace->IsQuadratic())
8852
8853   else { // theFace is quadratic
8854     // we have to split theFace on simple triangles and one simple quadrangle
8855     int tmp = il1/2;
8856     int nbshift = tmp*2;
8857     // shift nodes in nodes[] by nbshift
8858     int i,j;
8859     for(i=0; i<nbshift; i++) {
8860       const SMDS_MeshNode* n = nodes[0];
8861       for(j=0; j<nbFaceNodes-1; j++) {
8862         nodes[j] = nodes[j+1];
8863       }
8864       nodes[nbFaceNodes-1] = n;
8865     }
8866     il1 = il1 - nbshift;
8867     // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
8868     //   n0      n1     n2    n0      n1     n2
8869     //     +-----+-----+        +-----+-----+
8870     //      \         /         |           |
8871     //       \       /          |           |
8872     //      n5+     +n3       n7+           +n3
8873     //         \   /            |           |
8874     //          \ /             |           |
8875     //           +              +-----+-----+
8876     //           n4           n6      n5     n4
8877
8878     // create new elements
8879     int n1,n2,n3;
8880     if ( nbFaceNodes == 6 ) { // quadratic triangle
8881       newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8882       if ( theFace->IsMediumNode(nodes[il1]) ) {
8883         // create quadrangle
8884         newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[5] ));
8885         n1 = 1;
8886         n2 = 2;
8887         n3 = 3;
8888       }
8889       else {
8890         // create quadrangle
8891         newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[5] ));
8892         n1 = 0;
8893         n2 = 1;
8894         n3 = 5;
8895       }
8896     }
8897     else { // nbFaceNodes==8 - quadratic quadrangle
8898       newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8899       newElems.push_back( aMesh->AddFace( nodes[5], nodes[6], nodes[7] ));
8900       newElems.push_back( aMesh->AddFace( nodes[5], nodes[7], nodes[3] ));
8901       if ( theFace->IsMediumNode( nodes[ il1 ])) {
8902         // create quadrangle
8903         newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[7] ));
8904         n1 = 1;
8905         n2 = 2;
8906         n3 = 3;
8907       }
8908       else {
8909         // create quadrangle
8910         newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[7] ));
8911         n1 = 0;
8912         n2 = 1;
8913         n3 = 7;
8914       }
8915     }
8916     // create needed triangles using n1,n2,n3 and inserted nodes
8917     int nbn = 2 + aNodesToInsert.size();
8918     vector<const SMDS_MeshNode*> aNodes(nbn);
8919     aNodes[0    ] = nodes[n1];
8920     aNodes[nbn-1] = nodes[n2];
8921     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8922     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8923       aNodes[iNode++] = *nIt;
8924     }
8925     for ( i = 1; i < nbn; i++ )
8926       newElems.push_back( aMesh->AddFace( aNodes[i-1], aNodes[i], nodes[n3] ));
8927   }
8928
8929   // remove the old face
8930   for ( size_t i = 0; i < newElems.size(); ++i )
8931     if ( newElems[i] )
8932     {
8933       aMesh->SetMeshElementOnShape( newElems[i], theFace->getshapeId() );
8934       myLastCreatedElems.Append( newElems[i] );
8935     }
8936   ReplaceElemInGroups( theFace, newElems, aMesh );
8937   aMesh->RemoveElement(theFace);
8938
8939 } // InsertNodesIntoLink()
8940
8941 //=======================================================================
8942 //function : UpdateVolumes
8943 //purpose  :
8944 //=======================================================================
8945
8946 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode*        theBetweenNode1,
8947                                       const SMDS_MeshNode*        theBetweenNode2,
8948                                       list<const SMDS_MeshNode*>& theNodesToInsert)
8949 {
8950   myLastCreatedElems.Clear();
8951   myLastCreatedNodes.Clear();
8952
8953   SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
8954   while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
8955     const SMDS_MeshElement* elem = invElemIt->next();
8956
8957     // check, if current volume has link theBetweenNode1 - theBetweenNode2
8958     SMDS_VolumeTool aVolume (elem);
8959     if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
8960       continue;
8961
8962     // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
8963     int iface, nbFaces = aVolume.NbFaces();
8964     vector<const SMDS_MeshNode *> poly_nodes;
8965     vector<int> quantities (nbFaces);
8966
8967     for (iface = 0; iface < nbFaces; iface++) {
8968       int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
8969       // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
8970       const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
8971
8972       for (int inode = 0; inode < nbFaceNodes; inode++) {
8973         poly_nodes.push_back(faceNodes[inode]);
8974
8975         if (nbInserted == 0) {
8976           if (faceNodes[inode] == theBetweenNode1) {
8977             if (faceNodes[inode + 1] == theBetweenNode2) {
8978               nbInserted = theNodesToInsert.size();
8979
8980               // add nodes to insert
8981               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
8982               for (; nIt != theNodesToInsert.end(); nIt++) {
8983                 poly_nodes.push_back(*nIt);
8984               }
8985             }
8986           }
8987           else if (faceNodes[inode] == theBetweenNode2) {
8988             if (faceNodes[inode + 1] == theBetweenNode1) {
8989               nbInserted = theNodesToInsert.size();
8990
8991               // add nodes to insert in reversed order
8992               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
8993               nIt--;
8994               for (; nIt != theNodesToInsert.begin(); nIt--) {
8995                 poly_nodes.push_back(*nIt);
8996               }
8997               poly_nodes.push_back(*nIt);
8998             }
8999           }
9000           else {
9001           }
9002         }
9003       }
9004       quantities[iface] = nbFaceNodes + nbInserted;
9005     }
9006
9007     // Replace the volume
9008     SMESHDS_Mesh *aMesh = GetMeshDS();
9009
9010     if ( SMDS_MeshElement* newElem = aMesh->AddPolyhedralVolume( poly_nodes, quantities ))
9011     {
9012       aMesh->SetMeshElementOnShape( newElem, elem->getshapeId() );
9013       myLastCreatedElems.Append( newElem );
9014       ReplaceElemInGroups( elem, newElem, aMesh );
9015     }
9016     aMesh->RemoveElement( elem );
9017   }
9018 }
9019
9020 namespace
9021 {
9022   //================================================================================
9023   /*!
9024    * \brief Transform any volume into data of SMDSEntity_Polyhedra
9025    */
9026   //================================================================================
9027
9028   void volumeToPolyhedron( const SMDS_MeshElement*         elem,
9029                            vector<const SMDS_MeshNode *> & nodes,
9030                            vector<int> &                   nbNodeInFaces )
9031   {
9032     nodes.clear();
9033     nbNodeInFaces.clear();
9034     SMDS_VolumeTool vTool ( elem );
9035     for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
9036     {
9037       const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
9038       nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
9039       nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
9040     }
9041   }
9042 }
9043
9044 //=======================================================================
9045 /*!
9046  * \brief Convert elements contained in a sub-mesh to quadratic
9047  * \return int - nb of checked elements
9048  */
9049 //=======================================================================
9050
9051 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh *   theSm,
9052                                              SMESH_MesherHelper& theHelper,
9053                                              const bool          theForce3d)
9054 {
9055   int nbElem = 0;
9056   if( !theSm ) return nbElem;
9057
9058   vector<int> nbNodeInFaces;
9059   vector<const SMDS_MeshNode *> nodes;
9060   SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
9061   while(ElemItr->more())
9062   {
9063     nbElem++;
9064     const SMDS_MeshElement* elem = ElemItr->next();
9065     if( !elem ) continue;
9066
9067     // analyse a necessity of conversion
9068     const SMDSAbs_ElementType aType = elem->GetType();
9069     if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
9070       continue;
9071     const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
9072     bool hasCentralNodes = false;
9073     if ( elem->IsQuadratic() )
9074     {
9075       bool alreadyOK;
9076       switch ( aGeomType ) {
9077       case SMDSEntity_Quad_Triangle:
9078       case SMDSEntity_Quad_Quadrangle:
9079       case SMDSEntity_Quad_Hexa:
9080         alreadyOK = !theHelper.GetIsBiQuadratic(); break;
9081
9082       case SMDSEntity_BiQuad_Triangle:
9083       case SMDSEntity_BiQuad_Quadrangle:
9084       case SMDSEntity_TriQuad_Hexa:
9085         alreadyOK = theHelper.GetIsBiQuadratic();
9086         hasCentralNodes = true;
9087         break;
9088       default:
9089         alreadyOK = true;
9090       }
9091       // take into account already present modium nodes
9092       switch ( aType ) {
9093       case SMDSAbs_Volume:
9094         theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
9095       case SMDSAbs_Face:
9096         theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
9097       case SMDSAbs_Edge:
9098         theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
9099       default:;
9100       }
9101       if ( alreadyOK )
9102         continue;
9103     }
9104     // get elem data needed to re-create it
9105     //
9106     const int id      = elem->GetID();
9107     const int nbNodes = elem->NbCornerNodes();
9108     nodes.assign(elem->begin_nodes(), elem->end_nodes());
9109     if ( aGeomType == SMDSEntity_Polyhedra )
9110       nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
9111     else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
9112       volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9113
9114     // remove a linear element
9115     GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9116
9117     // remove central nodes of biquadratic elements (biquad->quad convertion)
9118     if ( hasCentralNodes )
9119       for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
9120         if ( nodes[i]->NbInverseElements() == 0 )
9121           GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
9122
9123     const SMDS_MeshElement* NewElem = 0;
9124
9125     switch( aType )
9126     {
9127     case SMDSAbs_Edge :
9128       {
9129         NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9130         break;
9131       }
9132     case SMDSAbs_Face :
9133       {
9134         switch(nbNodes)
9135         {
9136         case 3:
9137           NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9138           break;
9139         case 4:
9140           NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9141           break;
9142         default:
9143           NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9144         }
9145         break;
9146       }
9147     case SMDSAbs_Volume :
9148       {
9149         switch( aGeomType )
9150         {
9151         case SMDSEntity_Tetra:
9152           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9153           break;
9154         case SMDSEntity_Pyramid:
9155           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9156           break;
9157         case SMDSEntity_Penta:
9158           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9159           break;
9160         case SMDSEntity_Hexa:
9161         case SMDSEntity_Quad_Hexa:
9162         case SMDSEntity_TriQuad_Hexa:
9163           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9164                                         nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9165           break;
9166         case SMDSEntity_Hexagonal_Prism:
9167         default:
9168           NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9169         }
9170         break;
9171       }
9172     default :
9173       continue;
9174     }
9175     ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9176     if( NewElem && NewElem->getshapeId() < 1 )
9177       theSm->AddElement( NewElem );
9178   }
9179   return nbElem;
9180 }
9181 //=======================================================================
9182 //function : ConvertToQuadratic
9183 //purpose  :
9184 //=======================================================================
9185
9186 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
9187 {
9188   SMESHDS_Mesh* meshDS = GetMeshDS();
9189
9190   SMESH_MesherHelper aHelper(*myMesh);
9191
9192   aHelper.SetIsQuadratic( true );
9193   aHelper.SetIsBiQuadratic( theToBiQuad );
9194   aHelper.SetElementsOnShape(true);
9195   aHelper.ToFixNodeParameters( true );
9196
9197   // convert elements assigned to sub-meshes
9198   int nbCheckedElems = 0;
9199   if ( myMesh->HasShapeToMesh() )
9200   {
9201     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9202     {
9203       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9204       while ( smIt->more() ) {
9205         SMESH_subMesh* sm = smIt->next();
9206         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9207           aHelper.SetSubShape( sm->GetSubShape() );
9208           nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9209         }
9210       }
9211     }
9212   }
9213
9214   // convert elements NOT assigned to sub-meshes
9215   int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9216   if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
9217   {
9218     aHelper.SetElementsOnShape(false);
9219     SMESHDS_SubMesh *smDS = 0;
9220
9221     // convert edges
9222     SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9223     while( aEdgeItr->more() )
9224     {
9225       const SMDS_MeshEdge* edge = aEdgeItr->next();
9226       if ( !edge->IsQuadratic() )
9227       {
9228         int                  id = edge->GetID();
9229         const SMDS_MeshNode* n1 = edge->GetNode(0);
9230         const SMDS_MeshNode* n2 = edge->GetNode(1);
9231
9232         meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9233
9234         const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9235         ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9236       }
9237       else
9238       {
9239         aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
9240       }
9241     }
9242
9243     // convert faces
9244     SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9245     while( aFaceItr->more() )
9246     {
9247       const SMDS_MeshFace* face = aFaceItr->next();
9248       if ( !face ) continue;
9249       
9250       const SMDSAbs_EntityType type = face->GetEntityType();
9251       bool alreadyOK;
9252       switch( type )
9253       {
9254       case SMDSEntity_Quad_Triangle:
9255       case SMDSEntity_Quad_Quadrangle:
9256         alreadyOK = !theToBiQuad;
9257         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9258         break;
9259       case SMDSEntity_BiQuad_Triangle:
9260       case SMDSEntity_BiQuad_Quadrangle:
9261         alreadyOK = theToBiQuad;
9262         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9263         break;
9264       default: alreadyOK = false;
9265       }
9266       if ( alreadyOK )
9267         continue;
9268
9269       const int id = face->GetID();
9270       vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9271
9272       meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9273
9274       SMDS_MeshFace * NewFace = 0;
9275       switch( type )
9276       {
9277       case SMDSEntity_Triangle:
9278       case SMDSEntity_Quad_Triangle:
9279       case SMDSEntity_BiQuad_Triangle:
9280         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9281         if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
9282           GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
9283         break;
9284
9285       case SMDSEntity_Quadrangle:
9286       case SMDSEntity_Quad_Quadrangle:
9287       case SMDSEntity_BiQuad_Quadrangle:
9288         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9289         if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
9290           GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
9291         break;
9292
9293       default:;
9294         NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9295       }
9296       ReplaceElemInGroups( face, NewFace, GetMeshDS());
9297     }
9298
9299     // convert volumes
9300     vector<int> nbNodeInFaces;
9301     SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9302     while(aVolumeItr->more())
9303     {
9304       const SMDS_MeshVolume* volume = aVolumeItr->next();
9305       if ( !volume ) continue;
9306
9307       const SMDSAbs_EntityType type = volume->GetEntityType();
9308       if ( volume->IsQuadratic() )
9309       {
9310         bool alreadyOK;
9311         switch ( type )
9312         {
9313         case SMDSEntity_Quad_Hexa:    alreadyOK = !theToBiQuad; break;
9314         case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9315         default:                      alreadyOK = true;
9316         }
9317         if ( alreadyOK )
9318         {
9319           aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
9320           continue;
9321         }
9322       }
9323       const int id = volume->GetID();
9324       vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9325       if ( type == SMDSEntity_Polyhedra )
9326         nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
9327       else if ( type == SMDSEntity_Hexagonal_Prism )
9328         volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9329
9330       meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9331
9332       SMDS_MeshVolume * NewVolume = 0;
9333       switch ( type )
9334       {
9335       case SMDSEntity_Tetra:
9336         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9337         break;
9338       case SMDSEntity_Hexa:
9339       case SMDSEntity_Quad_Hexa:
9340       case SMDSEntity_TriQuad_Hexa:
9341         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9342                                       nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9343         for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
9344           if ( nodes[i]->NbInverseElements() == 0 )
9345             GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9346         break;
9347       case SMDSEntity_Pyramid:
9348         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9349                                       nodes[3], nodes[4], id, theForce3d);
9350         break;
9351       case SMDSEntity_Penta:
9352         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9353                                       nodes[3], nodes[4], nodes[5], id, theForce3d);
9354         break;
9355       case SMDSEntity_Hexagonal_Prism:
9356       default:
9357         NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9358       }
9359       ReplaceElemInGroups(volume, NewVolume, meshDS);
9360     }
9361   }
9362
9363   if ( !theForce3d )
9364   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9365     // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9366     // aHelper.FixQuadraticElements(myError);
9367     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9368   }
9369 }
9370
9371 //================================================================================
9372 /*!
9373  * \brief Makes given elements quadratic
9374  *  \param theForce3d - if true, the medium nodes will be placed in the middle of link
9375  *  \param theElements - elements to make quadratic
9376  */
9377 //================================================================================
9378
9379 void SMESH_MeshEditor::ConvertToQuadratic(const bool        theForce3d,
9380                                           TIDSortedElemSet& theElements,
9381                                           const bool        theToBiQuad)
9382 {
9383   if ( theElements.empty() ) return;
9384
9385   // we believe that all theElements are of the same type
9386   const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9387
9388   // get all nodes shared by theElements
9389   TIDSortedNodeSet allNodes;
9390   TIDSortedElemSet::iterator eIt = theElements.begin();
9391   for ( ; eIt != theElements.end(); ++eIt )
9392     allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9393
9394   // complete theElements with elements of lower dim whose all nodes are in allNodes
9395
9396   TIDSortedElemSet quadAdjacentElems    [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9397   TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9398   TIDSortedNodeSet::iterator nIt = allNodes.begin();
9399   for ( ; nIt != allNodes.end(); ++nIt )
9400   {
9401     const SMDS_MeshNode* n = *nIt;
9402     SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9403     while ( invIt->more() )
9404     {
9405       const SMDS_MeshElement*      e = invIt->next();
9406       const SMDSAbs_ElementType type = e->GetType();
9407       if ( e->IsQuadratic() )
9408       {
9409         quadAdjacentElems[ type ].insert( e );
9410
9411         bool alreadyOK;
9412         switch ( e->GetEntityType() ) {
9413         case SMDSEntity_Quad_Triangle:
9414         case SMDSEntity_Quad_Quadrangle:
9415         case SMDSEntity_Quad_Hexa:         alreadyOK = !theToBiQuad; break;
9416         case SMDSEntity_BiQuad_Triangle:
9417         case SMDSEntity_BiQuad_Quadrangle:
9418         case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; break;
9419         default:                           alreadyOK = true;
9420         }
9421         if ( alreadyOK )
9422           continue;
9423       }
9424       if ( type >= elemType )
9425         continue; // same type or more complex linear element
9426
9427       if ( !checkedAdjacentElems[ type ].insert( e ).second )
9428         continue; // e is already checked
9429
9430       // check nodes
9431       bool allIn = true;
9432       SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9433       while ( nodeIt->more() && allIn )
9434         allIn = allNodes.count( nodeIt->next() );
9435       if ( allIn )
9436         theElements.insert(e );
9437     }
9438   }
9439
9440   SMESH_MesherHelper helper(*myMesh);
9441   helper.SetIsQuadratic( true );
9442   helper.SetIsBiQuadratic( theToBiQuad );
9443
9444   // add links of quadratic adjacent elements to the helper
9445
9446   if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9447     for ( eIt  = quadAdjacentElems[SMDSAbs_Edge].begin();
9448           eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9449     {
9450       helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9451     }
9452   if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9453     for ( eIt  = quadAdjacentElems[SMDSAbs_Face].begin();
9454           eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9455     {
9456       helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9457     }
9458   if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9459     for ( eIt  = quadAdjacentElems[SMDSAbs_Volume].begin();
9460           eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9461     {
9462       helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9463     }
9464
9465   // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9466
9467   SMESHDS_Mesh*  meshDS = GetMeshDS();
9468   SMESHDS_SubMesh* smDS = 0;
9469   for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9470   {
9471     const SMDS_MeshElement* elem = *eIt;
9472
9473     bool alreadyOK;
9474     int nbCentralNodes = 0;
9475     switch ( elem->GetEntityType() ) {
9476       // linear convertible
9477     case SMDSEntity_Edge:
9478     case SMDSEntity_Triangle:
9479     case SMDSEntity_Quadrangle:
9480     case SMDSEntity_Tetra:
9481     case SMDSEntity_Pyramid:
9482     case SMDSEntity_Hexa:
9483     case SMDSEntity_Penta:             alreadyOK = false;       nbCentralNodes = 0; break;
9484       // quadratic that can become bi-quadratic
9485     case SMDSEntity_Quad_Triangle:
9486     case SMDSEntity_Quad_Quadrangle:
9487     case SMDSEntity_Quad_Hexa:         alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9488       // bi-quadratic
9489     case SMDSEntity_BiQuad_Triangle:
9490     case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9491     case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9492       // the rest
9493     default:                           alreadyOK = true;
9494     }
9495     if ( alreadyOK ) continue;
9496
9497     const SMDSAbs_ElementType type = elem->GetType();
9498     const int                   id = elem->GetID();
9499     const int              nbNodes = elem->NbCornerNodes();
9500     vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9501
9502     helper.SetSubShape( elem->getshapeId() );
9503
9504     if ( !smDS || !smDS->Contains( elem ))
9505       smDS = meshDS->MeshElements( elem->getshapeId() );
9506     meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9507
9508     SMDS_MeshElement * newElem = 0;
9509     switch( nbNodes )
9510     {
9511     case 4: // cases for most frequently used element types go first (for optimization)
9512       if ( type == SMDSAbs_Volume )
9513         newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9514       else
9515         newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9516       break;
9517     case 8:
9518       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9519                                  nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9520       break;
9521     case 3:
9522       newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], id, theForce3d);
9523       break;
9524     case 2:
9525       newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9526       break;
9527     case 5:
9528       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9529                                  nodes[4], id, theForce3d);
9530       break;
9531     case 6:
9532       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9533                                  nodes[4], nodes[5], id, theForce3d);
9534       break;
9535     default:;
9536     }
9537     ReplaceElemInGroups( elem, newElem, meshDS);
9538     if( newElem && smDS )
9539       smDS->AddElement( newElem );
9540
9541      // remove central nodes
9542     for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9543       if ( nodes[i]->NbInverseElements() == 0 )
9544         meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9545
9546   } // loop on theElements
9547
9548   if ( !theForce3d )
9549   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9550     // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9551     // helper.FixQuadraticElements( myError );
9552     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9553   }
9554 }
9555
9556 //=======================================================================
9557 /*!
9558  * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9559  * \return int - nb of checked elements
9560  */
9561 //=======================================================================
9562
9563 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh *    theSm,
9564                                      SMDS_ElemIteratorPtr theItr,
9565                                      const int            theShapeID)
9566 {
9567   int nbElem = 0;
9568   SMESHDS_Mesh* meshDS = GetMeshDS();
9569   ElemFeatures elemType;
9570   vector<const SMDS_MeshNode *> nodes;
9571
9572   while( theItr->more() )
9573   {
9574     const SMDS_MeshElement* elem = theItr->next();
9575     nbElem++;
9576     if( elem && elem->IsQuadratic())
9577     {
9578       // get elem data
9579       int nbCornerNodes = elem->NbCornerNodes();
9580       nodes.assign( elem->begin_nodes(), elem->end_nodes() );
9581
9582       elemType.Init( elem, /*basicOnly=*/false ).SetID( elem->GetID() ).SetQuad( false );
9583
9584       //remove a quadratic element
9585       if ( !theSm || !theSm->Contains( elem ))
9586         theSm = meshDS->MeshElements( elem->getshapeId() );
9587       meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9588
9589       // remove medium nodes
9590       for ( size_t i = nbCornerNodes; i < nodes.size(); ++i )
9591         if ( nodes[i]->NbInverseElements() == 0 )
9592           meshDS->RemoveFreeNode( nodes[i], theSm );
9593
9594       // add a linear element
9595       nodes.resize( nbCornerNodes );
9596       SMDS_MeshElement * newElem = AddElement( nodes, elemType );
9597       ReplaceElemInGroups(elem, newElem, meshDS);
9598       if( theSm && newElem )
9599         theSm->AddElement( newElem );
9600     }
9601   }
9602   return nbElem;
9603 }
9604
9605 //=======================================================================
9606 //function : ConvertFromQuadratic
9607 //purpose  :
9608 //=======================================================================
9609
9610 bool SMESH_MeshEditor::ConvertFromQuadratic()
9611 {
9612   int nbCheckedElems = 0;
9613   if ( myMesh->HasShapeToMesh() )
9614   {
9615     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9616     {
9617       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9618       while ( smIt->more() ) {
9619         SMESH_subMesh* sm = smIt->next();
9620         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9621           nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9622       }
9623     }
9624   }
9625
9626   int totalNbElems =
9627     GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9628   if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9629   {
9630     SMESHDS_SubMesh *aSM = 0;
9631     removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9632   }
9633
9634   return true;
9635 }
9636
9637 namespace
9638 {
9639   //================================================================================
9640   /*!
9641    * \brief Return true if all medium nodes of the element are in the node set
9642    */
9643   //================================================================================
9644
9645   bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9646   {
9647     for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9648       if ( !nodeSet.count( elem->GetNode(i) ))
9649         return false;
9650     return true;
9651   }
9652 }
9653
9654 //================================================================================
9655 /*!
9656  * \brief Makes given elements linear
9657  */
9658 //================================================================================
9659
9660 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9661 {
9662   if ( theElements.empty() ) return;
9663
9664   // collect IDs of medium nodes of theElements; some of these nodes will be removed
9665   set<int> mediumNodeIDs;
9666   TIDSortedElemSet::iterator eIt = theElements.begin();
9667   for ( ; eIt != theElements.end(); ++eIt )
9668   {
9669     const SMDS_MeshElement* e = *eIt;
9670     for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9671       mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9672   }
9673
9674   // replace given elements by linear ones
9675   SMDS_ElemIteratorPtr elemIt = elemSetIterator( theElements );
9676   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9677
9678   // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9679   // except those elements sharing medium nodes of quadratic element whose medium nodes
9680   // are not all in mediumNodeIDs
9681
9682   // get remaining medium nodes
9683   TIDSortedNodeSet mediumNodes;
9684   set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9685   for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9686     if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9687       mediumNodes.insert( mediumNodes.end(), n );
9688
9689   // find more quadratic elements to convert
9690   TIDSortedElemSet moreElemsToConvert;
9691   TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9692   for ( ; nIt != mediumNodes.end(); ++nIt )
9693   {
9694     SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9695     while ( invIt->more() )
9696     {
9697       const SMDS_MeshElement* e = invIt->next();
9698       if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9699       {
9700         // find a more complex element including e and
9701         // whose medium nodes are not in mediumNodes
9702         bool complexFound = false;
9703         for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9704         {
9705           SMDS_ElemIteratorPtr invIt2 =
9706             (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9707           while ( invIt2->more() )
9708           {
9709             const SMDS_MeshElement* eComplex = invIt2->next();
9710             if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9711             {
9712               int nbCommonNodes = SMESH_MeshAlgos::GetCommonNodes( e, eComplex ).size();
9713               if ( nbCommonNodes == e->NbNodes())
9714               {
9715                 complexFound = true;
9716                 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9717                 break;
9718               }
9719             }
9720           }
9721         }
9722         if ( !complexFound )
9723           moreElemsToConvert.insert( e );
9724       }
9725     }
9726   }
9727   elemIt = elemSetIterator( moreElemsToConvert );
9728   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9729 }
9730
9731 //=======================================================================
9732 //function : SewSideElements
9733 //purpose  :
9734 //=======================================================================
9735
9736 SMESH_MeshEditor::Sew_Error
9737 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet&    theSide1,
9738                                    TIDSortedElemSet&    theSide2,
9739                                    const SMDS_MeshNode* theFirstNode1,
9740                                    const SMDS_MeshNode* theFirstNode2,
9741                                    const SMDS_MeshNode* theSecondNode1,
9742                                    const SMDS_MeshNode* theSecondNode2)
9743 {
9744   myLastCreatedElems.Clear();
9745   myLastCreatedNodes.Clear();
9746
9747   if ( theSide1.size() != theSide2.size() )
9748     return SEW_DIFF_NB_OF_ELEMENTS;
9749
9750   Sew_Error aResult = SEW_OK;
9751   // Algo:
9752   // 1. Build set of faces representing each side
9753   // 2. Find which nodes of the side 1 to merge with ones on the side 2
9754   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9755
9756   // =======================================================================
9757   // 1. Build set of faces representing each side:
9758   // =======================================================================
9759   // a. build set of nodes belonging to faces
9760   // b. complete set of faces: find missing faces whose nodes are in set of nodes
9761   // c. create temporary faces representing side of volumes if correspondent
9762   //    face does not exist
9763
9764   SMESHDS_Mesh* aMesh = GetMeshDS();
9765   // TODO algoritm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9766   //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9767   TIDSortedElemSet             faceSet1, faceSet2;
9768   set<const SMDS_MeshElement*> volSet1,  volSet2;
9769   set<const SMDS_MeshNode*>    nodeSet1, nodeSet2;
9770   TIDSortedElemSet             * faceSetPtr[] = { &faceSet1, &faceSet2 };
9771   set<const SMDS_MeshElement*> *  volSetPtr[] = { &volSet1,  &volSet2  };
9772   set<const SMDS_MeshNode*>    * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9773   TIDSortedElemSet             * elemSetPtr[] = { &theSide1, &theSide2 };
9774   int iSide, iFace, iNode;
9775
9776   list<const SMDS_MeshElement* > tempFaceList;
9777   for ( iSide = 0; iSide < 2; iSide++ ) {
9778     set<const SMDS_MeshNode*>    * nodeSet = nodeSetPtr[ iSide ];
9779     TIDSortedElemSet             * elemSet = elemSetPtr[ iSide ];
9780     TIDSortedElemSet             * faceSet = faceSetPtr[ iSide ];
9781     set<const SMDS_MeshElement*> * volSet  = volSetPtr [ iSide ];
9782     set<const SMDS_MeshElement*>::iterator vIt;
9783     TIDSortedElemSet::iterator eIt;
9784     set<const SMDS_MeshNode*>::iterator    nIt;
9785
9786     // check that given nodes belong to given elements
9787     const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9788     const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9789     int firstIndex = -1, secondIndex = -1;
9790     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9791       const SMDS_MeshElement* elem = *eIt;
9792       if ( firstIndex  < 0 ) firstIndex  = elem->GetNodeIndex( n1 );
9793       if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9794       if ( firstIndex > -1 && secondIndex > -1 ) break;
9795     }
9796     if ( firstIndex < 0 || secondIndex < 0 ) {
9797       // we can simply return until temporary faces created
9798       return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9799     }
9800
9801     // -----------------------------------------------------------
9802     // 1a. Collect nodes of existing faces
9803     //     and build set of face nodes in order to detect missing
9804     //     faces corresponding to sides of volumes
9805     // -----------------------------------------------------------
9806
9807     set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9808
9809     // loop on the given element of a side
9810     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9811       //const SMDS_MeshElement* elem = *eIt;
9812       const SMDS_MeshElement* elem = *eIt;
9813       if ( elem->GetType() == SMDSAbs_Face ) {
9814         faceSet->insert( elem );
9815         set <const SMDS_MeshNode*> faceNodeSet;
9816         SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9817         while ( nodeIt->more() ) {
9818           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9819           nodeSet->insert( n );
9820           faceNodeSet.insert( n );
9821         }
9822         setOfFaceNodeSet.insert( faceNodeSet );
9823       }
9824       else if ( elem->GetType() == SMDSAbs_Volume )
9825         volSet->insert( elem );
9826     }
9827     // ------------------------------------------------------------------------------
9828     // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9829     // ------------------------------------------------------------------------------
9830
9831     for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9832       SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9833       while ( fIt->more() ) { // loop on faces sharing a node
9834         const SMDS_MeshElement* f = fIt->next();
9835         if ( faceSet->find( f ) == faceSet->end() ) {
9836           // check if all nodes are in nodeSet and
9837           // complete setOfFaceNodeSet if they are
9838           set <const SMDS_MeshNode*> faceNodeSet;
9839           SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9840           bool allInSet = true;
9841           while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9842             const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9843             if ( nodeSet->find( n ) == nodeSet->end() )
9844               allInSet = false;
9845             else
9846               faceNodeSet.insert( n );
9847           }
9848           if ( allInSet ) {
9849             faceSet->insert( f );
9850             setOfFaceNodeSet.insert( faceNodeSet );
9851           }
9852         }
9853       }
9854     }
9855
9856     // -------------------------------------------------------------------------
9857     // 1c. Create temporary faces representing sides of volumes if correspondent
9858     //     face does not exist
9859     // -------------------------------------------------------------------------
9860
9861     if ( !volSet->empty() ) {
9862       //int nodeSetSize = nodeSet->size();
9863
9864       // loop on given volumes
9865       for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
9866         SMDS_VolumeTool vol (*vIt);
9867         // loop on volume faces: find free faces
9868         // --------------------------------------
9869         list<const SMDS_MeshElement* > freeFaceList;
9870         for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
9871           if ( !vol.IsFreeFace( iFace ))
9872             continue;
9873           // check if there is already a face with same nodes in a face set
9874           const SMDS_MeshElement* aFreeFace = 0;
9875           const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
9876           int nbNodes = vol.NbFaceNodes( iFace );
9877           set <const SMDS_MeshNode*> faceNodeSet;
9878           vol.GetFaceNodes( iFace, faceNodeSet );
9879           bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
9880           if ( isNewFace ) {
9881             // no such a face is given but it still can exist, check it
9882             vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
9883             aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
9884           }
9885           if ( !aFreeFace ) {
9886             // create a temporary face
9887             if ( nbNodes == 3 ) {
9888               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
9889               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
9890             }
9891             else if ( nbNodes == 4 ) {
9892               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9893               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9894             }
9895             else {
9896               vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9897               //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
9898               aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
9899             }
9900             if ( aFreeFace )
9901               tempFaceList.push_back( aFreeFace );
9902           }
9903
9904           if ( aFreeFace )
9905             freeFaceList.push_back( aFreeFace );
9906
9907         } // loop on faces of a volume
9908
9909         // choose one of several free faces of a volume
9910         // --------------------------------------------
9911         if ( freeFaceList.size() > 1 ) {
9912           // choose a face having max nb of nodes shared by other elems of a side
9913           int maxNbNodes = -1;
9914           list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
9915           while ( fIt != freeFaceList.end() ) { // loop on free faces
9916             int nbSharedNodes = 0;
9917             SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9918             while ( nodeIt->more() ) { // loop on free face nodes
9919               const SMDS_MeshNode* n =
9920                 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9921               SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
9922               while ( invElemIt->more() ) {
9923                 const SMDS_MeshElement* e = invElemIt->next();
9924                 nbSharedNodes += faceSet->count( e );
9925                 nbSharedNodes += elemSet->count( e );
9926               }
9927             }
9928             if ( nbSharedNodes > maxNbNodes ) {
9929               maxNbNodes = nbSharedNodes;
9930               freeFaceList.erase( freeFaceList.begin(), fIt++ );
9931             }
9932             else if ( nbSharedNodes == maxNbNodes ) {
9933               fIt++;
9934             }
9935             else {
9936               freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
9937             }
9938           }
9939           if ( freeFaceList.size() > 1 )
9940           {
9941             // could not choose one face, use another way
9942             // choose a face most close to the bary center of the opposite side
9943             gp_XYZ aBC( 0., 0., 0. );
9944             set <const SMDS_MeshNode*> addedNodes;
9945             TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
9946             eIt = elemSet2->begin();
9947             for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
9948               SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
9949               while ( nodeIt->more() ) { // loop on free face nodes
9950                 const SMDS_MeshNode* n =
9951                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9952                 if ( addedNodes.insert( n ).second )
9953                   aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
9954               }
9955             }
9956             aBC /= addedNodes.size();
9957             double minDist = DBL_MAX;
9958             fIt = freeFaceList.begin();
9959             while ( fIt != freeFaceList.end() ) { // loop on free faces
9960               double dist = 0;
9961               SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9962               while ( nodeIt->more() ) { // loop on free face nodes
9963                 const SMDS_MeshNode* n =
9964                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9965                 gp_XYZ p( n->X(),n->Y(),n->Z() );
9966                 dist += ( aBC - p ).SquareModulus();
9967               }
9968               if ( dist < minDist ) {
9969                 minDist = dist;
9970                 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9971               }
9972               else
9973                 fIt = freeFaceList.erase( fIt++ );
9974             }
9975           }
9976         } // choose one of several free faces of a volume
9977
9978         if ( freeFaceList.size() == 1 ) {
9979           const SMDS_MeshElement* aFreeFace = freeFaceList.front();
9980           faceSet->insert( aFreeFace );
9981           // complete a node set with nodes of a found free face
9982           //           for ( iNode = 0; iNode < ; iNode++ )
9983           //             nodeSet->insert( fNodes[ iNode ] );
9984         }
9985
9986       } // loop on volumes of a side
9987
9988       //       // complete a set of faces if new nodes in a nodeSet appeared
9989       //       // ----------------------------------------------------------
9990       //       if ( nodeSetSize != nodeSet->size() ) {
9991       //         for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9992       //           SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9993       //           while ( fIt->more() ) { // loop on faces sharing a node
9994       //             const SMDS_MeshElement* f = fIt->next();
9995       //             if ( faceSet->find( f ) == faceSet->end() ) {
9996       //               // check if all nodes are in nodeSet and
9997       //               // complete setOfFaceNodeSet if they are
9998       //               set <const SMDS_MeshNode*> faceNodeSet;
9999       //               SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10000       //               bool allInSet = true;
10001       //               while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10002       //                 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10003       //                 if ( nodeSet->find( n ) == nodeSet->end() )
10004       //                   allInSet = false;
10005       //                 else
10006       //                   faceNodeSet.insert( n );
10007       //               }
10008       //               if ( allInSet ) {
10009       //                 faceSet->insert( f );
10010       //                 setOfFaceNodeSet.insert( faceNodeSet );
10011       //               }
10012       //             }
10013       //           }
10014       //         }
10015       //       }
10016     } // Create temporary faces, if there are volumes given
10017   } // loop on sides
10018
10019   if ( faceSet1.size() != faceSet2.size() ) {
10020     // delete temporary faces: they are in reverseElements of actual nodes
10021 //    SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10022 //    while ( tmpFaceIt->more() )
10023 //      aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10024 //    list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10025 //    for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10026 //      aMesh->RemoveElement(*tmpFaceIt);
10027     MESSAGE("Diff nb of faces");
10028     return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10029   }
10030
10031   // ============================================================
10032   // 2. Find nodes to merge:
10033   //              bind a node to remove to a node to put instead
10034   // ============================================================
10035
10036   TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
10037   if ( theFirstNode1 != theFirstNode2 )
10038     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10039   if ( theSecondNode1 != theSecondNode2 )
10040     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10041
10042   LinkID_Gen aLinkID_Gen( GetMeshDS() );
10043   set< long > linkIdSet; // links to process
10044   linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
10045
10046   typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
10047   list< NLink > linkList[2];
10048   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10049   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10050   // loop on links in linkList; find faces by links and append links
10051   // of the found faces to linkList
10052   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10053   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
10054   {
10055     NLink link[] = { *linkIt[0], *linkIt[1] };
10056     long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
10057     if ( !linkIdSet.count( linkID ) )
10058       continue;
10059
10060     // by links, find faces in the face sets,
10061     // and find indices of link nodes in the found faces;
10062     // in a face set, there is only one or no face sharing a link
10063     // ---------------------------------------------------------------
10064
10065     const SMDS_MeshElement* face[] = { 0, 0 };
10066     vector<const SMDS_MeshNode*> fnodes[2];
10067     int iLinkNode[2][2];
10068     TIDSortedElemSet avoidSet;
10069     for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10070       const SMDS_MeshNode* n1 = link[iSide].first;
10071       const SMDS_MeshNode* n2 = link[iSide].second;
10072       //cout << "Side " << iSide << " ";
10073       //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
10074       // find a face by two link nodes
10075       face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
10076                                                       *faceSetPtr[ iSide ], avoidSet,
10077                                                       &iLinkNode[iSide][0],
10078                                                       &iLinkNode[iSide][1] );
10079       if ( face[ iSide ])
10080       {
10081         //cout << " F " << face[ iSide]->GetID() <<endl;
10082         faceSetPtr[ iSide ]->erase( face[ iSide ]);
10083         // put face nodes to fnodes
10084         if ( face[ iSide ]->IsQuadratic() )
10085         {
10086           // use interlaced nodes iterator
10087           const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>( face[ iSide ]);
10088           if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
10089           SMDS_ElemIteratorPtr nIter = F->interlacedNodesElemIterator();
10090           while ( nIter->more() )
10091             fnodes[ iSide ].push_back( cast2Node( nIter->next() ));
10092         }
10093         else
10094         {
10095           fnodes[ iSide ].assign( face[ iSide ]->begin_nodes(),
10096                                   face[ iSide ]->end_nodes() );
10097         }
10098         fnodes[ iSide ].push_back( fnodes[ iSide ].front());
10099       }
10100     }
10101
10102     // check similarity of elements of the sides
10103     if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10104       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10105       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10106         aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10107       }
10108       else {
10109         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10110       }
10111       break; // do not return because it's necessary to remove tmp faces
10112     }
10113
10114     // set nodes to merge
10115     // -------------------
10116
10117     if ( face[0] && face[1] )  {
10118       const int nbNodes = face[0]->NbNodes();
10119       if ( nbNodes != face[1]->NbNodes() ) {
10120         MESSAGE("Diff nb of face nodes");
10121         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10122         break; // do not return because it s necessary to remove tmp faces
10123       }
10124       bool reverse[] = { false, false }; // order of nodes in the link
10125       for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10126         // analyse link orientation in faces
10127         int i1 = iLinkNode[ iSide ][ 0 ];
10128         int i2 = iLinkNode[ iSide ][ 1 ];
10129         reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10130       }
10131       int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
10132       int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
10133       for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
10134       {
10135         nReplaceMap.insert  ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
10136                                           fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
10137       }
10138
10139       // add other links of the faces to linkList
10140       // -----------------------------------------
10141
10142       for ( iNode = 0; iNode < nbNodes; iNode++ )  {
10143         linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
10144         pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10145         if ( !iter_isnew.second ) { // already in a set: no need to process
10146           linkIdSet.erase( iter_isnew.first );
10147         }
10148         else // new in set == encountered for the first time: add
10149         {
10150           const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
10151           const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
10152           linkList[0].push_back ( NLink( n1, n2 ));
10153           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10154         }
10155       }
10156     } // 2 faces found
10157
10158     if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
10159       break;
10160
10161   } // loop on link lists
10162
10163   if ( aResult == SEW_OK &&
10164        ( //linkIt[0] != linkList[0].end() ||
10165          !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10166     MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10167              " " << (faceSetPtr[1]->empty()));
10168     aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10169   }
10170
10171   // ====================================================================
10172   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10173   // ====================================================================
10174
10175   // delete temporary faces
10176 //  SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10177 //  while ( tmpFaceIt->more() )
10178 //    aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10179   list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10180   for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10181     aMesh->RemoveElement(*tmpFaceIt);
10182
10183   if ( aResult != SEW_OK)
10184     return aResult;
10185
10186   list< int > nodeIDsToRemove;
10187   vector< const SMDS_MeshNode*> nodes;
10188   ElemFeatures elemType;
10189
10190   // loop on nodes replacement map
10191   TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10192   for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10193     if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second )
10194     {
10195       const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10196       nodeIDsToRemove.push_back( nToRemove->GetID() );
10197       // loop on elements sharing nToRemove
10198       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10199       while ( invElemIt->more() ) {
10200         const SMDS_MeshElement* e = invElemIt->next();
10201         // get a new suite of nodes: make replacement
10202         int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10203         nodes.resize( nbNodes );
10204         SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10205         while ( nIt->more() ) {
10206           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
10207           nnIt = nReplaceMap.find( n );
10208           if ( nnIt != nReplaceMap.end() ) {
10209             nbReplaced++;
10210             n = (*nnIt).second;
10211           }
10212           nodes[ i++ ] = n;
10213         }
10214         //       if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10215         //         elemIDsToRemove.push_back( e->GetID() );
10216         //       else
10217         if ( nbReplaced )
10218         {
10219           elemType.Init( e, /*basicOnly=*/false ).SetID( e->GetID() );
10220           aMesh->RemoveElement( e );
10221
10222           if ( SMDS_MeshElement* newElem = this->AddElement( nodes, elemType ))
10223           {
10224             AddToSameGroups( newElem, e, aMesh );
10225             if ( int aShapeId = e->getshapeId() )
10226               aMesh->SetMeshElementOnShape( newElem, aShapeId );
10227           }
10228         }
10229       }
10230     }
10231
10232   Remove( nodeIDsToRemove, true );
10233
10234   return aResult;
10235 }
10236
10237 //================================================================================
10238 /*!
10239  * \brief Find corresponding nodes in two sets of faces
10240  * \param theSide1 - first face set
10241  * \param theSide2 - second first face
10242  * \param theFirstNode1 - a boundary node of set 1
10243  * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10244  * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10245  * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10246  * \param nReplaceMap - output map of corresponding nodes
10247  * \return bool  - is a success or not
10248  */
10249 //================================================================================
10250
10251 #ifdef _DEBUG_
10252 //#define DEBUG_MATCHING_NODES
10253 #endif
10254
10255 SMESH_MeshEditor::Sew_Error
10256 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10257                                     set<const SMDS_MeshElement*>& theSide2,
10258                                     const SMDS_MeshNode*          theFirstNode1,
10259                                     const SMDS_MeshNode*          theFirstNode2,
10260                                     const SMDS_MeshNode*          theSecondNode1,
10261                                     const SMDS_MeshNode*          theSecondNode2,
10262                                     TNodeNodeMap &                nReplaceMap)
10263 {
10264   set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10265
10266   nReplaceMap.clear();
10267   if ( theFirstNode1 != theFirstNode2 )
10268     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10269   if ( theSecondNode1 != theSecondNode2 )
10270     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10271
10272   set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10273   linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10274
10275   list< NLink > linkList[2];
10276   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10277   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10278
10279   // loop on links in linkList; find faces by links and append links
10280   // of the found faces to linkList
10281   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10282   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10283     NLink link[] = { *linkIt[0], *linkIt[1] };
10284     if ( linkSet.find( link[0] ) == linkSet.end() )
10285       continue;
10286
10287     // by links, find faces in the face sets,
10288     // and find indices of link nodes in the found faces;
10289     // in a face set, there is only one or no face sharing a link
10290     // ---------------------------------------------------------------
10291
10292     const SMDS_MeshElement* face[] = { 0, 0 };
10293     list<const SMDS_MeshNode*> notLinkNodes[2];
10294     //bool reverse[] = { false, false }; // order of notLinkNodes
10295     int nbNodes[2];
10296     for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10297     {
10298       const SMDS_MeshNode* n1 = link[iSide].first;
10299       const SMDS_MeshNode* n2 = link[iSide].second;
10300       set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10301       set< const SMDS_MeshElement* > facesOfNode1;
10302       for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10303       {
10304         // during a loop of the first node, we find all faces around n1,
10305         // during a loop of the second node, we find one face sharing both n1 and n2
10306         const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10307         SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10308         while ( fIt->more() ) { // loop on faces sharing a node
10309           const SMDS_MeshElement* f = fIt->next();
10310           if (faceSet->find( f ) != faceSet->end() && // f is in face set
10311               ! facesOfNode1.insert( f ).second ) // f encounters twice
10312           {
10313             if ( face[ iSide ] ) {
10314               MESSAGE( "2 faces per link " );
10315               return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10316             }
10317             face[ iSide ] = f;
10318             faceSet->erase( f );
10319
10320             // get not link nodes
10321             int nbN = f->NbNodes();
10322             if ( f->IsQuadratic() )
10323               nbN /= 2;
10324             nbNodes[ iSide ] = nbN;
10325             list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10326             int i1 = f->GetNodeIndex( n1 );
10327             int i2 = f->GetNodeIndex( n2 );
10328             int iEnd = nbN, iBeg = -1, iDelta = 1;
10329             bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10330             if ( reverse ) {
10331               std::swap( iEnd, iBeg ); iDelta = -1;
10332             }
10333             int i = i2;
10334             while ( true ) {
10335               i += iDelta;
10336               if ( i == iEnd ) i = iBeg + iDelta;
10337               if ( i == i1 ) break;
10338               nodes.push_back ( f->GetNode( i ) );
10339             }
10340           }
10341         }
10342       }
10343     }
10344     // check similarity of elements of the sides
10345     if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10346       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10347       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10348         return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10349       }
10350       else {
10351         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10352       }
10353     }
10354
10355     // set nodes to merge
10356     // -------------------
10357
10358     if ( face[0] && face[1] )  {
10359       if ( nbNodes[0] != nbNodes[1] ) {
10360         MESSAGE("Diff nb of face nodes");
10361         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10362       }
10363 #ifdef DEBUG_MATCHING_NODES
10364       MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10365                 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10366                 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10367 #endif
10368       int nbN = nbNodes[0];
10369       {
10370         list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10371         list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10372         for ( int i = 0 ; i < nbN - 2; ++i ) {
10373 #ifdef DEBUG_MATCHING_NODES
10374           MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10375 #endif
10376           nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10377         }
10378       }
10379
10380       // add other links of the face 1 to linkList
10381       // -----------------------------------------
10382
10383       const SMDS_MeshElement* f0 = face[0];
10384       const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10385       for ( int i = 0; i < nbN; i++ )
10386       {
10387         const SMDS_MeshNode* n2 = f0->GetNode( i );
10388         pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10389           linkSet.insert( SMESH_TLink( n1, n2 ));
10390         if ( !iter_isnew.second ) { // already in a set: no need to process
10391           linkSet.erase( iter_isnew.first );
10392         }
10393         else // new in set == encountered for the first time: add
10394         {
10395 #ifdef DEBUG_MATCHING_NODES
10396           MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10397                     << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10398 #endif
10399           linkList[0].push_back ( NLink( n1, n2 ));
10400           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10401         }
10402         n1 = n2;
10403       }
10404     } // 2 faces found
10405   } // loop on link lists
10406
10407   return SEW_OK;
10408 }
10409
10410 //================================================================================
10411 /*!
10412  * \brief Create elements equal (on same nodes) to given ones
10413  *  \param [in] theElements - a set of elems to duplicate. If it is empty, all
10414  *              elements of the uppest dimension are duplicated.
10415  */
10416 //================================================================================
10417
10418 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
10419 {
10420   ClearLastCreated();
10421   SMESHDS_Mesh* mesh = GetMeshDS();
10422
10423   // get an element type and an iterator over elements
10424
10425   SMDSAbs_ElementType type = SMDSAbs_All;
10426   SMDS_ElemIteratorPtr elemIt;
10427   vector< const SMDS_MeshElement* > allElems;
10428   if ( theElements.empty() )
10429   {
10430     if ( mesh->NbNodes() == 0 )
10431       return;
10432     // get most complex type
10433     SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10434       SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10435       SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10436     };
10437     for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10438       if ( mesh->GetMeshInfo().NbElements( types[i] ))
10439       {
10440         type = types[i];
10441         break;
10442       }
10443     // put all elements in the vector <allElems>
10444     allElems.reserve( mesh->GetMeshInfo().NbElements( type ));
10445     elemIt = mesh->elementsIterator( type );
10446     while ( elemIt->more() )
10447       allElems.push_back( elemIt->next());
10448     elemIt = elemSetIterator( allElems );
10449   }
10450   else
10451   {
10452     type = (*theElements.begin())->GetType();
10453     elemIt = elemSetIterator( theElements );
10454   }
10455
10456   // duplicate elements
10457
10458   ElemFeatures elemType;
10459
10460   vector< const SMDS_MeshNode* > nodes;
10461   while ( elemIt->more() )
10462   {
10463     const SMDS_MeshElement* elem = elemIt->next();
10464     if ( elem->GetType() != type )
10465       continue;
10466
10467     elemType.Init( elem, /*basicOnly=*/false );
10468     nodes.assign( elem->begin_nodes(), elem->end_nodes() );
10469
10470     AddElement( nodes, elemType );
10471   }
10472 }
10473
10474 //================================================================================
10475 /*!
10476   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10477   \param theElems - the list of elements (edges or faces) to be replicated
10478   The nodes for duplication could be found from these elements
10479   \param theNodesNot - list of nodes to NOT replicate
10480   \param theAffectedElems - the list of elements (cells and edges) to which the
10481   replicated nodes should be associated to.
10482   \return TRUE if operation has been completed successfully, FALSE otherwise
10483 */
10484 //================================================================================
10485
10486 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10487                                     const TIDSortedElemSet& theNodesNot,
10488                                     const TIDSortedElemSet& theAffectedElems )
10489 {
10490   myLastCreatedElems.Clear();
10491   myLastCreatedNodes.Clear();
10492
10493   if ( theElems.size() == 0 )
10494     return false;
10495
10496   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10497   if ( !aMeshDS )
10498     return false;
10499
10500   bool res = false;
10501   TNodeNodeMap anOldNodeToNewNode;
10502   // duplicate elements and nodes
10503   res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10504   // replce nodes by duplications
10505   res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10506   return res;
10507 }
10508
10509 //================================================================================
10510 /*!
10511   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10512   \param theMeshDS - mesh instance
10513   \param theElems - the elements replicated or modified (nodes should be changed)
10514   \param theNodesNot - nodes to NOT replicate
10515   \param theNodeNodeMap - relation of old node to new created node
10516   \param theIsDoubleElem - flag os to replicate element or modify
10517   \return TRUE if operation has been completed successfully, FALSE otherwise
10518 */
10519 //================================================================================
10520
10521 bool SMESH_MeshEditor::doubleNodes(SMESHDS_Mesh*           theMeshDS,
10522                                    const TIDSortedElemSet& theElems,
10523                                    const TIDSortedElemSet& theNodesNot,
10524                                    TNodeNodeMap&           theNodeNodeMap,
10525                                    const bool              theIsDoubleElem )
10526 {
10527   // iterate through element and duplicate them (by nodes duplication)
10528   bool res = false;
10529   std::vector<const SMDS_MeshNode*> newNodes;
10530   ElemFeatures elemType;
10531
10532   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10533   for ( ;  elemItr != theElems.end(); ++elemItr )
10534   {
10535     const SMDS_MeshElement* anElem = *elemItr;
10536     if (!anElem)
10537       continue;
10538
10539     // duplicate nodes to duplicate element
10540     bool isDuplicate = false;
10541     newNodes.resize( anElem->NbNodes() );
10542     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10543     int ind = 0;
10544     while ( anIter->more() )
10545     {
10546       const SMDS_MeshNode* aCurrNode = static_cast<const SMDS_MeshNode*>( anIter->next() );
10547       const SMDS_MeshNode*  aNewNode = aCurrNode;
10548       TNodeNodeMap::iterator     n2n = theNodeNodeMap.find( aCurrNode );
10549       if ( n2n != theNodeNodeMap.end() )
10550       {
10551         aNewNode = n2n->second;
10552       }
10553       else if ( theIsDoubleElem && !theNodesNot.count( aCurrNode ))
10554       {
10555         // duplicate node
10556         aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10557         copyPosition( aCurrNode, aNewNode );
10558         theNodeNodeMap[ aCurrNode ] = aNewNode;
10559         myLastCreatedNodes.Append( aNewNode );
10560       }
10561       isDuplicate |= (aCurrNode != aNewNode);
10562       newNodes[ ind++ ] = aNewNode;
10563     }
10564     if ( !isDuplicate )
10565       continue;
10566
10567     if ( theIsDoubleElem )
10568       AddElement( newNodes, elemType.Init( anElem, /*basicOnly=*/false ));
10569     else
10570       theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], newNodes.size() );
10571
10572     res = true;
10573   }
10574   return res;
10575 }
10576
10577 //================================================================================
10578 /*!
10579   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10580   \param theNodes - identifiers of nodes to be doubled
10581   \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10582   nodes. If list of element identifiers is empty then nodes are doubled but
10583   they not assigned to elements
10584   \return TRUE if operation has been completed successfully, FALSE otherwise
10585 */
10586 //================================================================================
10587
10588 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10589                                     const std::list< int >& theListOfModifiedElems )
10590 {
10591   myLastCreatedElems.Clear();
10592   myLastCreatedNodes.Clear();
10593
10594   if ( theListOfNodes.size() == 0 )
10595     return false;
10596
10597   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10598   if ( !aMeshDS )
10599     return false;
10600
10601   // iterate through nodes and duplicate them
10602
10603   std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10604
10605   std::list< int >::const_iterator aNodeIter;
10606   for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10607   {
10608     int aCurr = *aNodeIter;
10609     SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr );
10610     if ( !aNode )
10611       continue;
10612
10613     // duplicate node
10614
10615     const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10616     if ( aNewNode )
10617     {
10618       copyPosition( aNode, aNewNode );
10619       anOldNodeToNewNode[ aNode ] = aNewNode;
10620       myLastCreatedNodes.Append( aNewNode );
10621     }
10622   }
10623
10624   // Create map of new nodes for modified elements
10625
10626   std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> > anElemToNodes;
10627
10628   std::list< int >::const_iterator anElemIter;
10629   for ( anElemIter = theListOfModifiedElems.begin();
10630         anElemIter != theListOfModifiedElems.end(); ++anElemIter )
10631   {
10632     int aCurr = *anElemIter;
10633     SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr );
10634     if ( !anElem )
10635       continue;
10636
10637     vector<const SMDS_MeshNode*> aNodeArr( anElem->NbNodes() );
10638
10639     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10640     int ind = 0;
10641     while ( anIter->more() )
10642     {
10643       SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10644       if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() )
10645       {
10646         const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ];
10647         aNodeArr[ ind++ ] = aNewNode;
10648       }
10649       else
10650         aNodeArr[ ind++ ] = aCurrNode;
10651     }
10652     anElemToNodes[ anElem ] = aNodeArr;
10653   }
10654
10655   // Change nodes of elements
10656
10657   std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> >::iterator
10658     anElemToNodesIter = anElemToNodes.begin();
10659   for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter )
10660   {
10661     const SMDS_MeshElement* anElem = anElemToNodesIter->first;
10662     vector<const SMDS_MeshNode*> aNodeArr = anElemToNodesIter->second;
10663     if ( anElem )
10664     {
10665       aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() );
10666     }
10667   }
10668
10669   return true;
10670 }
10671
10672 namespace {
10673
10674   //================================================================================
10675   /*!
10676   \brief Check if element located inside shape
10677   \return TRUE if IN or ON shape, FALSE otherwise
10678   */
10679   //================================================================================
10680
10681   template<class Classifier>
10682   bool isInside(const SMDS_MeshElement* theElem,
10683                 Classifier&             theClassifier,
10684                 const double            theTol)
10685   {
10686     gp_XYZ centerXYZ (0, 0, 0);
10687     SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
10688     while (aNodeItr->more())
10689       centerXYZ += SMESH_TNodeXYZ(cast2Node( aNodeItr->next()));
10690
10691     gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10692     theClassifier.Perform(aPnt, theTol);
10693     TopAbs_State aState = theClassifier.State();
10694     return (aState == TopAbs_IN || aState == TopAbs_ON );
10695   }
10696
10697   //================================================================================
10698   /*!
10699    * \brief Classifier of the 3D point on the TopoDS_Face
10700    *        with interaface suitable for isInside()
10701    */
10702   //================================================================================
10703
10704   struct _FaceClassifier
10705   {
10706     Extrema_ExtPS       _extremum;
10707     BRepAdaptor_Surface _surface;
10708     TopAbs_State        _state;
10709
10710     _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10711     {
10712       _extremum.Initialize( _surface,
10713                             _surface.FirstUParameter(), _surface.LastUParameter(),
10714                             _surface.FirstVParameter(), _surface.LastVParameter(),
10715                             _surface.Tolerance(), _surface.Tolerance() );
10716     }
10717     void Perform(const gp_Pnt& aPnt, double theTol)
10718     {
10719       theTol *= theTol;
10720       _state = TopAbs_OUT;
10721       _extremum.Perform(aPnt);
10722       if ( _extremum.IsDone() )
10723         for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10724           _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10725     }
10726     TopAbs_State State() const
10727     {
10728       return _state;
10729     }
10730   };
10731 }
10732
10733 //================================================================================
10734 /*!
10735   \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
10736   This method is the first step of DoubleNodeElemGroupsInRegion.
10737   \param theElems - list of groups of elements (edges or faces) to be replicated
10738   \param theNodesNot - list of groups of nodes not to replicated
10739   \param theShape - shape to detect affected elements (element which geometric center
10740          located on or inside shape). If the shape is null, detection is done on faces orientations
10741          (select elements with a gravity center on the side given by faces normals).
10742          This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
10743          The replicated nodes should be associated to affected elements.
10744   \return groups of affected elements
10745   \sa DoubleNodeElemGroupsInRegion()
10746  */
10747 //================================================================================
10748
10749 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
10750                                                    const TIDSortedElemSet& theNodesNot,
10751                                                    const TopoDS_Shape&     theShape,
10752                                                    TIDSortedElemSet&       theAffectedElems)
10753 {
10754   if ( theShape.IsNull() )
10755   {
10756     std::set<const SMDS_MeshNode*> alreadyCheckedNodes;
10757     std::set<const SMDS_MeshElement*> alreadyCheckedElems;
10758     std::set<const SMDS_MeshElement*> edgesToCheck;
10759     alreadyCheckedNodes.clear();
10760     alreadyCheckedElems.clear();
10761     edgesToCheck.clear();
10762
10763     // --- iterates on elements to be replicated and get elements by back references from their nodes
10764
10765     TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10766     for ( ;  elemItr != theElems.end(); ++elemItr )
10767     {
10768       SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10769       if (!anElem || (anElem->GetType() != SMDSAbs_Face))
10770         continue;
10771       gp_XYZ normal;
10772       SMESH_MeshAlgos::FaceNormal( anElem, normal, /*normalized=*/true );
10773       std::set<const SMDS_MeshNode*> nodesElem;
10774       nodesElem.clear();
10775       SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10776       while ( nodeItr->more() )
10777       {
10778         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10779         nodesElem.insert(aNode);
10780       }
10781       std::set<const SMDS_MeshNode*>::iterator nodit = nodesElem.begin();
10782       for (; nodit != nodesElem.end(); nodit++)
10783       {
10784         const SMDS_MeshNode* aNode = *nodit;
10785         if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10786           continue;
10787         if (alreadyCheckedNodes.find(aNode) != alreadyCheckedNodes.end())
10788           continue;
10789         alreadyCheckedNodes.insert(aNode);
10790         SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10791         while ( backElemItr->more() )
10792         {
10793           const SMDS_MeshElement* curElem = backElemItr->next();
10794           if (alreadyCheckedElems.find(curElem) != alreadyCheckedElems.end())
10795             continue;
10796           if (theElems.find(curElem) != theElems.end())
10797             continue;
10798           alreadyCheckedElems.insert(curElem);
10799           double x=0, y=0, z=0;
10800           int nb = 0;
10801           SMDS_ElemIteratorPtr nodeItr2 = curElem->nodesIterator();
10802           while ( nodeItr2->more() )
10803           {
10804             const SMDS_MeshNode* anotherNode = cast2Node(nodeItr2->next());
10805             x += anotherNode->X();
10806             y += anotherNode->Y();
10807             z += anotherNode->Z();
10808             nb++;
10809           }
10810           gp_XYZ p;
10811           p.SetCoord( x/nb -aNode->X(),
10812                       y/nb -aNode->Y(),
10813                       z/nb -aNode->Z() );
10814           if (normal*p > 0)
10815           {
10816             theAffectedElems.insert( curElem );
10817           }
10818           else if (curElem->GetType() == SMDSAbs_Edge)
10819             edgesToCheck.insert(curElem);
10820         }
10821       }
10822     }
10823     // --- add also edges lying on the set of faces (all nodes in alreadyCheckedNodes)
10824     std::set<const SMDS_MeshElement*>::iterator eit = edgesToCheck.begin();
10825     for( ; eit != edgesToCheck.end(); eit++)
10826     {
10827       bool onside = true;
10828       const SMDS_MeshElement* anEdge = *eit;
10829       SMDS_ElemIteratorPtr nodeItr = anEdge->nodesIterator();
10830       while ( nodeItr->more() )
10831       {
10832         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10833         if (alreadyCheckedNodes.find(aNode) == alreadyCheckedNodes.end())
10834         {
10835           onside = false;
10836           break;
10837         }
10838       }
10839       if (onside)
10840       {
10841         theAffectedElems.insert(anEdge);
10842       }
10843     }
10844   }
10845   else
10846   {
10847     const double aTol = Precision::Confusion();
10848     auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
10849     auto_ptr<_FaceClassifier>              aFaceClassifier;
10850     if ( theShape.ShapeType() == TopAbs_SOLID )
10851     {
10852       bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10853       bsc3d->PerformInfinitePoint(aTol);
10854     }
10855     else if (theShape.ShapeType() == TopAbs_FACE )
10856     {
10857       aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10858     }
10859
10860     // iterates on indicated elements and get elements by back references from their nodes
10861     TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10862     for ( ;  elemItr != theElems.end(); ++elemItr )
10863     {
10864       SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10865       if (!anElem)
10866         continue;
10867       SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10868       while ( nodeItr->more() )
10869       {
10870         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10871         if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10872           continue;
10873         SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10874         while ( backElemItr->more() )
10875         {
10876           const SMDS_MeshElement* curElem = backElemItr->next();
10877           if ( curElem && theElems.find(curElem) == theElems.end() &&
10878               ( bsc3d.get() ?
10879                 isInside( curElem, *bsc3d, aTol ) :
10880                 isInside( curElem, *aFaceClassifier, aTol )))
10881             theAffectedElems.insert( curElem );
10882         }
10883       }
10884     }
10885   }
10886   return true;
10887 }
10888
10889 //================================================================================
10890 /*!
10891   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10892   \param theElems - group of of elements (edges or faces) to be replicated
10893   \param theNodesNot - group of nodes not to replicate
10894   \param theShape - shape to detect affected elements (element which geometric center
10895   located on or inside shape).
10896   The replicated nodes should be associated to affected elements.
10897   \return TRUE if operation has been completed successfully, FALSE otherwise
10898 */
10899 //================================================================================
10900
10901 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
10902                                             const TIDSortedElemSet& theNodesNot,
10903                                             const TopoDS_Shape&     theShape )
10904 {
10905   if ( theShape.IsNull() )
10906     return false;
10907
10908   const double aTol = Precision::Confusion();
10909   SMESHUtils::Deleter< BRepClass3d_SolidClassifier> bsc3d;
10910   SMESHUtils::Deleter<_FaceClassifier>              aFaceClassifier;
10911   if ( theShape.ShapeType() == TopAbs_SOLID )
10912   {
10913     bsc3d._obj = new BRepClass3d_SolidClassifier( theShape );
10914     bsc3d->PerformInfinitePoint(aTol);
10915   }
10916   else if (theShape.ShapeType() == TopAbs_FACE )
10917   {
10918     aFaceClassifier._obj = new _FaceClassifier( TopoDS::Face( theShape ));
10919   }
10920
10921   // iterates on indicated elements and get elements by back references from their nodes
10922   TIDSortedElemSet anAffected;
10923   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10924   for ( ;  elemItr != theElems.end(); ++elemItr )
10925   {
10926     SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10927     if (!anElem)
10928       continue;
10929
10930     SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10931     while ( nodeItr->more() )
10932     {
10933       const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10934       if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10935         continue;
10936       SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10937       while ( backElemItr->more() )
10938       {
10939         const SMDS_MeshElement* curElem = backElemItr->next();
10940         if ( curElem && theElems.find(curElem) == theElems.end() &&
10941              ( bsc3d ?
10942                isInside( curElem, *bsc3d, aTol ) :
10943                isInside( curElem, *aFaceClassifier, aTol )))
10944           anAffected.insert( curElem );
10945       }
10946     }
10947   }
10948   return DoubleNodes( theElems, theNodesNot, anAffected );
10949 }
10950
10951 /*!
10952  *  \brief compute an oriented angle between two planes defined by four points.
10953  *  The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
10954  *  @param p0 base of the rotation axe
10955  *  @param p1 extremity of the rotation axe
10956  *  @param g1 belongs to the first plane
10957  *  @param g2 belongs to the second plane
10958  */
10959 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
10960 {
10961   gp_Vec vref(p0, p1);
10962   gp_Vec v1(p0, g1);
10963   gp_Vec v2(p0, g2);
10964   gp_Vec n1 = vref.Crossed(v1);
10965   gp_Vec n2 = vref.Crossed(v2);
10966   try {
10967     return n2.AngleWithRef(n1, vref);
10968   }
10969   catch ( Standard_Failure ) {
10970   }
10971   return Max( v1.Magnitude(), v2.Magnitude() );
10972 }
10973
10974 /*!
10975  * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
10976  *  The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
10977  * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
10978  * Triangles are transformed into prisms, and quadrangles into hexahedrons.
10979  * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
10980  * 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.
10981  * 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.
10982  * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
10983  * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
10984  * \param theElems - list of groups of volumes, where a group of volume is a set of
10985  *        SMDS_MeshElements sorted by Id.
10986  * \param createJointElems - if TRUE, create the elements
10987  * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
10988  *        the boundary between \a theDomains and the rest mesh
10989  * \return TRUE if operation has been completed successfully, FALSE otherwise
10990  */
10991 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
10992                                                      bool                                 createJointElems,
10993                                                      bool                                 onAllBoundaries)
10994 {
10995   MESSAGE("----------------------------------------------");
10996   MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
10997   MESSAGE("----------------------------------------------");
10998
10999   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11000   meshDS->BuildDownWardConnectivity(true);
11001   CHRONO(50);
11002   SMDS_UnstructuredGrid *grid = meshDS->getGrid();
11003
11004   // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11005   //     build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11006   //     build the list of nodes shared by 2 or more domains, with their domain indexes
11007
11008   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11009   std::map<int,int>celldom; // cell vtkId --> domain
11010   std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains;  // oldNode --> (id domain --> id cell)
11011   std::map<int, std::map<int,int> > nodeDomains; // oldId -->  (domainId --> newId)
11012   faceDomains.clear();
11013   celldom.clear();
11014   cellDomains.clear();
11015   nodeDomains.clear();
11016   std::map<int,int> emptyMap;
11017   std::set<int> emptySet;
11018   emptyMap.clear();
11019
11020   MESSAGE(".. Number of domains :"<<theElems.size());
11021
11022   TIDSortedElemSet theRestDomElems;
11023   const int iRestDom  = -1;
11024   const int idom0     = onAllBoundaries ? iRestDom : 0;
11025   const int nbDomains = theElems.size();
11026
11027   // Check if the domains do not share an element
11028   for (int idom = 0; idom < nbDomains-1; idom++)
11029   {
11030     //       MESSAGE("... Check of domain #" << idom);
11031     const TIDSortedElemSet& domain = theElems[idom];
11032     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11033     for (; elemItr != domain.end(); ++elemItr)
11034     {
11035       const SMDS_MeshElement* anElem = *elemItr;
11036       int idombisdeb = idom + 1 ;
11037       // check if the element belongs to a domain further in the list
11038       for ( size_t idombis = idombisdeb; idombis < theElems.size(); idombis++ )
11039       {
11040         const TIDSortedElemSet& domainbis = theElems[idombis];
11041         if ( domainbis.count( anElem ))
11042         {
11043           MESSAGE(".... Domain #" << idom);
11044           MESSAGE(".... Domain #" << idombis);
11045           throw SALOME_Exception("The domains are not disjoint.");
11046           return false ;
11047         }
11048       }
11049     }
11050   }
11051
11052   for (int idom = 0; idom < nbDomains; idom++)
11053   {
11054
11055     // --- build a map (face to duplicate --> volume to modify)
11056     //     with all the faces shared by 2 domains (group of elements)
11057     //     and corresponding volume of this domain, for each shared face.
11058     //     a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11059
11060     MESSAGE("... Neighbors of domain #" << idom);
11061     const TIDSortedElemSet& domain = theElems[idom];
11062     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11063     for (; elemItr != domain.end(); ++elemItr)
11064     {
11065       const SMDS_MeshElement* anElem = *elemItr;
11066       if (!anElem)
11067         continue;
11068       int vtkId = anElem->getVtkId();
11069       //MESSAGE("  vtkId " << vtkId << " smdsId " << anElem->GetID());
11070       int neighborsVtkIds[NBMAXNEIGHBORS];
11071       int downIds[NBMAXNEIGHBORS];
11072       unsigned char downTypes[NBMAXNEIGHBORS];
11073       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11074       for (int n = 0; n < nbNeighbors; n++)
11075       {
11076         int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
11077         const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11078         if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
11079         {
11080           bool ok = false;
11081           for ( size_t idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
11082           {
11083             // MESSAGE("Domain " << idombis);
11084             const TIDSortedElemSet& domainbis = theElems[idombis];
11085             if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11086           }
11087           if ( ok || onAllBoundaries ) // the characteristics of the face is stored
11088           {
11089             DownIdType face(downIds[n], downTypes[n]);
11090             if (!faceDomains[face].count(idom))
11091             {
11092               faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11093               celldom[vtkId] = idom;
11094               //MESSAGE("       cell with a border " << vtkId << " domain " << idom);
11095             }
11096             if ( !ok )
11097             {
11098               theRestDomElems.insert( elem );
11099               faceDomains[face][iRestDom] = neighborsVtkIds[n];
11100               celldom[neighborsVtkIds[n]] = iRestDom;
11101             }
11102           }
11103         }
11104       }
11105     }
11106   }
11107
11108   //MESSAGE("Number of shared faces " << faceDomains.size());
11109   std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11110
11111   // --- explore the shared faces domain by domain,
11112   //     explore the nodes of the face and see if they belong to a cell in the domain,
11113   //     which has only a node or an edge on the border (not a shared face)
11114
11115   for (int idomain = idom0; idomain < nbDomains; idomain++)
11116   {
11117     //MESSAGE("Domain " << idomain);
11118     const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
11119     itface = faceDomains.begin();
11120     for (; itface != faceDomains.end(); ++itface)
11121     {
11122       const std::map<int, int>& domvol = itface->second;
11123       if (!domvol.count(idomain))
11124         continue;
11125       DownIdType face = itface->first;
11126       //MESSAGE(" --- face " << face.cellId);
11127       std::set<int> oldNodes;
11128       oldNodes.clear();
11129       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11130       std::set<int>::iterator itn = oldNodes.begin();
11131       for (; itn != oldNodes.end(); ++itn)
11132       {
11133         int oldId = *itn;
11134         //MESSAGE("     node " << oldId);
11135         vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11136         for (int i=0; i<l.ncells; i++)
11137         {
11138           int vtkId = l.cells[i];
11139           const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
11140           if (!domain.count(anElem))
11141             continue;
11142           int vtkType = grid->GetCellType(vtkId);
11143           int downId = grid->CellIdToDownId(vtkId);
11144           if (downId < 0)
11145           {
11146             MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11147             continue; // not OK at this stage of the algorithm:
11148             //no cells created after BuildDownWardConnectivity
11149           }
11150           DownIdType aCell(downId, vtkType);
11151           cellDomains[aCell][idomain] = vtkId;
11152           celldom[vtkId] = idomain;
11153           //MESSAGE("       cell " << vtkId << " domain " << idomain);
11154         }
11155       }
11156     }
11157   }
11158
11159   // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11160   //     for each shared face, get the nodes
11161   //     for each node, for each domain of the face, create a clone of the node
11162
11163   // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11164   //     junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11165   //     the value is the ordered domain ids. (more than 4 domains not taken into account)
11166
11167   std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11168   std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11169   std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11170
11171   MESSAGE(".. Duplication of the nodes");
11172   for (int idomain = idom0; idomain < nbDomains; idomain++)
11173   {
11174     itface = faceDomains.begin();
11175     for (; itface != faceDomains.end(); ++itface)
11176     {
11177       const std::map<int, int>& domvol = itface->second;
11178       if (!domvol.count(idomain))
11179         continue;
11180       DownIdType face = itface->first;
11181       //MESSAGE(" --- face " << face.cellId);
11182       std::set<int> oldNodes;
11183       oldNodes.clear();
11184       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11185       std::set<int>::iterator itn = oldNodes.begin();
11186       for (; itn != oldNodes.end(); ++itn)
11187       {
11188         int oldId = *itn;
11189         if (nodeDomains[oldId].empty())
11190         {
11191           nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11192           //MESSAGE("-+-+-b     oldNode " << oldId << " domain " << idomain);
11193         }
11194         std::map<int, int>::const_iterator itdom = domvol.begin();
11195         for (; itdom != domvol.end(); ++itdom)
11196         {
11197           int idom = itdom->first;
11198           //MESSAGE("         domain " << idom);
11199           if (!nodeDomains[oldId].count(idom)) // --- node to clone
11200           {
11201             if (nodeDomains[oldId].size() >= 2) // a multiple node
11202             {
11203               vector<int> orderedDoms;
11204               //MESSAGE("multiple node " << oldId);
11205               if (mutipleNodes.count(oldId))
11206                 orderedDoms = mutipleNodes[oldId];
11207               else
11208               {
11209                 map<int,int>::iterator it = nodeDomains[oldId].begin();
11210                 for (; it != nodeDomains[oldId].end(); ++it)
11211                   orderedDoms.push_back(it->first);
11212               }
11213               orderedDoms.push_back(idom); // TODO order ==> push_front or back
11214               //stringstream txt;
11215               //for (int i=0; i<orderedDoms.size(); i++)
11216               //  txt << orderedDoms[i] << " ";
11217               //MESSAGE("orderedDoms " << txt.str());
11218               mutipleNodes[oldId] = orderedDoms;
11219             }
11220             double *coords = grid->GetPoint(oldId);
11221             SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11222             copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11223             int newId = newNode->getVtkId();
11224             nodeDomains[oldId][idom] = newId; // cloned node for other domains
11225             //MESSAGE("-+-+-c     oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11226           }
11227         }
11228       }
11229     }
11230   }
11231
11232   MESSAGE(".. Creation of elements");
11233   for (int idomain = idom0; idomain < nbDomains; idomain++)
11234   {
11235     itface = faceDomains.begin();
11236     for (; itface != faceDomains.end(); ++itface)
11237     {
11238       std::map<int, int> domvol = itface->second;
11239       if (!domvol.count(idomain))
11240         continue;
11241       DownIdType face = itface->first;
11242       //MESSAGE(" --- face " << face.cellId);
11243       std::set<int> oldNodes;
11244       oldNodes.clear();
11245       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11246       int nbMultipleNodes = 0;
11247       std::set<int>::iterator itn = oldNodes.begin();
11248       for (; itn != oldNodes.end(); ++itn)
11249       {
11250         int oldId = *itn;
11251         if (mutipleNodes.count(oldId))
11252           nbMultipleNodes++;
11253       }
11254       if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11255       {
11256         //MESSAGE("multiple Nodes detected on a shared face");
11257         int downId = itface->first.cellId;
11258         unsigned char cellType = itface->first.cellType;
11259         // --- shared edge or shared face ?
11260         if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11261         {
11262           int nodes[3];
11263           int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11264           for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11265             if (mutipleNodes.count(nodes[i]))
11266               if (!mutipleNodesToFace.count(nodes[i]))
11267                 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11268         }
11269         else // shared face (between two volumes)
11270         {
11271           int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11272           const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11273           const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11274           for (int ie =0; ie < nbEdges; ie++)
11275           {
11276             int nodes[3];
11277             int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11278             if ( mutipleNodes.count(nodes[0]) && mutipleNodes.count( nodes[ nbNodes-1 ]))
11279             {
11280               vector<int> vn0 = mutipleNodes[nodes[0]];
11281               vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11282               vector<int> doms;
11283               for ( size_t i0 = 0; i0 < vn0.size(); i0++ )
11284                 for ( size_t i1 = 0; i1 < vn1.size(); i1++ )
11285                   if ( vn0[i0] == vn1[i1] )
11286                     doms.push_back( vn0[ i0 ]);
11287               if ( doms.size() > 2 )
11288               {
11289                 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11290                 double *coords = grid->GetPoint(nodes[0]);
11291                 gp_Pnt p0(coords[0], coords[1], coords[2]);
11292                 coords = grid->GetPoint(nodes[nbNodes - 1]);
11293                 gp_Pnt p1(coords[0], coords[1], coords[2]);
11294                 gp_Pnt gref;
11295                 int vtkVolIds[1000];  // an edge can belong to a lot of volumes
11296                 map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
11297                 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11298                 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11299                 for ( size_t id = 0; id < doms.size(); id++ )
11300                 {
11301                   int idom = doms[id];
11302                   const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11303                   for ( int ivol = 0; ivol < nbvol; ivol++ )
11304                   {
11305                     int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
11306                     SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
11307                     if (domain.count(elem))
11308                     {
11309                       SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
11310                       domvol[idom] = svol;
11311                       //MESSAGE("  domain " << idom << " volume " << elem->GetID());
11312                       double values[3];
11313                       vtkIdType npts = 0;
11314                       vtkIdType* pts = 0;
11315                       grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11316                       SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
11317                       if (id ==0)
11318                       {
11319                         gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
11320                         angleDom[idom] = 0;
11321                       }
11322                       else
11323                       {
11324                         gp_Pnt g(values[0], values[1], values[2]);
11325                         angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11326                         //MESSAGE("  angle=" << angleDom[idom]);
11327                       }
11328                       break;
11329                     }
11330                   }
11331                 }
11332                 map<double, int> sortedDom; // sort domains by angle
11333                 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11334                   sortedDom[ia->second] = ia->first;
11335                 vector<int> vnodes;
11336                 vector<int> vdom;
11337                 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11338                 {
11339                   vdom.push_back(ib->second);
11340                   //MESSAGE("  ordered domain " << ib->second << "  angle " << ib->first);
11341                 }
11342                 for (int ino = 0; ino < nbNodes; ino++)
11343                   vnodes.push_back(nodes[ino]);
11344                 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11345               }
11346             }
11347           }
11348         }
11349       }
11350     }
11351   }
11352
11353   // --- iterate on shared faces (volumes to modify, face to extrude)
11354   //     get node id's of the face (id SMDS = id VTK)
11355   //     create flat element with old and new nodes if requested
11356
11357   // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11358   //     (domain1 X domain2) = domain1 + MAXINT*domain2
11359
11360   std::map<int, std::map<long,int> > nodeQuadDomains;
11361   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11362
11363   MESSAGE(".. Creation of elements: simple junction");
11364   if (createJointElems)
11365   {
11366     int idg;
11367     string joints2DName = "joints2D";
11368     mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
11369     SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11370     string joints3DName = "joints3D";
11371     mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
11372     SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11373
11374     itface = faceDomains.begin();
11375     for (; itface != faceDomains.end(); ++itface)
11376     {
11377       DownIdType face = itface->first;
11378       std::set<int> oldNodes;
11379       std::set<int>::iterator itn;
11380       oldNodes.clear();
11381       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11382
11383       std::map<int, int> domvol = itface->second;
11384       std::map<int, int>::iterator itdom = domvol.begin();
11385       int dom1 = itdom->first;
11386       int vtkVolId = itdom->second;
11387       itdom++;
11388       int dom2 = itdom->first;
11389       SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11390                                                        nodeQuadDomains);
11391       stringstream grpname;
11392       grpname << "j_";
11393       if (dom1 < dom2)
11394         grpname << dom1 << "_" << dom2;
11395       else
11396         grpname << dom2 << "_" << dom1;
11397       string namegrp = grpname.str();
11398       if (!mapOfJunctionGroups.count(namegrp))
11399         mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
11400       SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11401       if (sgrp)
11402         sgrp->Add(vol->GetID());
11403       if (vol->GetType() == SMDSAbs_Volume)
11404         joints3DGrp->Add(vol->GetID());
11405       else if (vol->GetType() == SMDSAbs_Face)
11406         joints2DGrp->Add(vol->GetID());
11407     }
11408   }
11409
11410   // --- create volumes on multiple domain intersection if requested
11411   //     iterate on mutipleNodesToFace
11412   //     iterate on edgesMultiDomains
11413
11414   MESSAGE(".. Creation of elements: multiple junction");
11415   if (createJointElems)
11416   {
11417     // --- iterate on mutipleNodesToFace
11418
11419     std::map<int, std::vector<int> >::iterator itn =  mutipleNodesToFace.begin();
11420     for (; itn != mutipleNodesToFace.end(); ++itn)
11421     {
11422       int node = itn->first;
11423       vector<int> orderDom = itn->second;
11424       vector<vtkIdType> orderedNodes;
11425       for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11426         orderedNodes.push_back( nodeDomains[ node ][ orderDom[ idom ]]);
11427       SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11428
11429       stringstream grpname;
11430       grpname << "m2j_";
11431       grpname << 0 << "_" << 0;
11432       int idg;
11433       string namegrp = grpname.str();
11434       if (!mapOfJunctionGroups.count(namegrp))
11435         mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
11436       SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11437       if (sgrp)
11438         sgrp->Add(face->GetID());
11439     }
11440
11441     // --- iterate on edgesMultiDomains
11442
11443     std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11444     for (; ite != edgesMultiDomains.end(); ++ite)
11445     {
11446       vector<int> nodes = ite->first;
11447       vector<int> orderDom = ite->second;
11448       vector<vtkIdType> orderedNodes;
11449       if (nodes.size() == 2)
11450       {
11451         //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11452         for ( size_t ino = 0; ino < nodes.size(); ino++ )
11453           if ( orderDom.size() == 3 )
11454             for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11455               orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11456           else
11457             for (int idom = orderDom.size()-1; idom >=0; idom--)
11458               orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11459         SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11460
11461         int idg;
11462         string namegrp = "jointsMultiples";
11463         if (!mapOfJunctionGroups.count(namegrp))
11464           mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11465         SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11466         if (sgrp)
11467           sgrp->Add(vol->GetID());
11468       }
11469       else
11470       {
11471         //INFOS("Quadratic multiple joints not implemented");
11472         // TODO quadratic nodes
11473       }
11474     }
11475   }
11476
11477   // --- list the explicit faces and edges of the mesh that need to be modified,
11478   //     i.e. faces and edges built with one or more duplicated nodes.
11479   //     associate these faces or edges to their corresponding domain.
11480   //     only the first domain found is kept when a face or edge is shared
11481
11482   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11483   std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11484   faceOrEdgeDom.clear();
11485   feDom.clear();
11486
11487   MESSAGE(".. Modification of elements");
11488   for (int idomain = idom0; idomain < nbDomains; idomain++)
11489   {
11490     std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11491     for (; itnod != nodeDomains.end(); ++itnod)
11492     {
11493       int oldId = itnod->first;
11494       //MESSAGE("     node " << oldId);
11495       vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11496       for (int i = 0; i < l.ncells; i++)
11497       {
11498         int vtkId = l.cells[i];
11499         int vtkType = grid->GetCellType(vtkId);
11500         int downId = grid->CellIdToDownId(vtkId);
11501         if (downId < 0)
11502           continue; // new cells: not to be modified
11503         DownIdType aCell(downId, vtkType);
11504         int volParents[1000];
11505         int nbvol = grid->GetParentVolumes(volParents, vtkId);
11506         for (int j = 0; j < nbvol; j++)
11507           if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11508             if (!feDom.count(vtkId))
11509             {
11510               feDom[vtkId] = idomain;
11511               faceOrEdgeDom[aCell] = emptyMap;
11512               faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11513               //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
11514               //        << " type " << vtkType << " downId " << downId);
11515             }
11516       }
11517     }
11518   }
11519
11520   // --- iterate on shared faces (volumes to modify, face to extrude)
11521   //     get node id's of the face
11522   //     replace old nodes by new nodes in volumes, and update inverse connectivity
11523
11524   std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11525   for (int m=0; m<3; m++)
11526   {
11527     std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11528     itface = (*amap).begin();
11529     for (; itface != (*amap).end(); ++itface)
11530     {
11531       DownIdType face = itface->first;
11532       std::set<int> oldNodes;
11533       std::set<int>::iterator itn;
11534       oldNodes.clear();
11535       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11536       //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11537       std::map<int, int> localClonedNodeIds;
11538
11539       std::map<int, int> domvol = itface->second;
11540       std::map<int, int>::iterator itdom = domvol.begin();
11541       for (; itdom != domvol.end(); ++itdom)
11542       {
11543         int idom = itdom->first;
11544         int vtkVolId = itdom->second;
11545         //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
11546         localClonedNodeIds.clear();
11547         for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11548         {
11549           int oldId = *itn;
11550           if (nodeDomains[oldId].count(idom))
11551           {
11552             localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11553             //MESSAGE("     node " << oldId << " --> " << localClonedNodeIds[oldId]);
11554           }
11555         }
11556         meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11557       }
11558     }
11559   }
11560
11561   // Remove empty groups (issue 0022812)
11562   std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
11563   for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
11564   {
11565     if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
11566       myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
11567   }
11568
11569   meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11570   grid->BuildLinks();
11571
11572   CHRONOSTOP(50);
11573   counters::stats();
11574   return true;
11575 }
11576
11577 /*!
11578  * \brief Double nodes on some external faces and create flat elements.
11579  * Flat elements are mainly used by some types of mechanic calculations.
11580  *
11581  * Each group of the list must be constituted of faces.
11582  * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11583  * @param theElems - list of groups of faces, where a group of faces is a set of
11584  * SMDS_MeshElements sorted by Id.
11585  * @return TRUE if operation has been completed successfully, FALSE otherwise
11586  */
11587 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11588 {
11589   MESSAGE("-------------------------------------------------");
11590   MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11591   MESSAGE("-------------------------------------------------");
11592
11593   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11594
11595   // --- For each group of faces
11596   //     duplicate the nodes, create a flat element based on the face
11597   //     replace the nodes of the faces by their clones
11598
11599   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11600   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11601   clonedNodes.clear();
11602   intermediateNodes.clear();
11603   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11604   mapOfJunctionGroups.clear();
11605
11606   for ( size_t idom = 0; idom < theElems.size(); idom++ )
11607   {
11608     const TIDSortedElemSet&           domain = theElems[idom];
11609     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11610     for ( ; elemItr != domain.end(); ++elemItr )
11611     {
11612       SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11613       SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
11614       if (!aFace)
11615         continue;
11616       // MESSAGE("aFace=" << aFace->GetID());
11617       bool isQuad = aFace->IsQuadratic();
11618       vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11619
11620       // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11621
11622       SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
11623       while (nodeIt->more())
11624       {
11625         const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
11626         bool isMedium = isQuad && (aFace->IsMediumNode(node));
11627         if (isMedium)
11628           ln2.push_back(node);
11629         else
11630           ln0.push_back(node);
11631
11632         const SMDS_MeshNode* clone = 0;
11633         if (!clonedNodes.count(node))
11634         {
11635           clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11636           copyPosition( node, clone );
11637           clonedNodes[node] = clone;
11638         }
11639         else
11640           clone = clonedNodes[node];
11641
11642         if (isMedium)
11643           ln3.push_back(clone);
11644         else
11645           ln1.push_back(clone);
11646
11647         const SMDS_MeshNode* inter = 0;
11648         if (isQuad && (!isMedium))
11649         {
11650           if (!intermediateNodes.count(node))
11651           {
11652             inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11653             copyPosition( node, inter );
11654             intermediateNodes[node] = inter;
11655           }
11656           else
11657             inter = intermediateNodes[node];
11658           ln4.push_back(inter);
11659         }
11660       }
11661
11662       // --- extrude the face
11663
11664       vector<const SMDS_MeshNode*> ln;
11665       SMDS_MeshVolume* vol = 0;
11666       vtkIdType aType = aFace->GetVtkType();
11667       switch (aType)
11668       {
11669       case VTK_TRIANGLE:
11670         vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11671         // MESSAGE("vol prism " << vol->GetID());
11672         ln.push_back(ln1[0]);
11673         ln.push_back(ln1[1]);
11674         ln.push_back(ln1[2]);
11675         break;
11676       case VTK_QUAD:
11677         vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11678         // MESSAGE("vol hexa " << vol->GetID());
11679         ln.push_back(ln1[0]);
11680         ln.push_back(ln1[1]);
11681         ln.push_back(ln1[2]);
11682         ln.push_back(ln1[3]);
11683         break;
11684       case VTK_QUADRATIC_TRIANGLE:
11685         vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11686                                 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11687         // MESSAGE("vol quad prism " << vol->GetID());
11688         ln.push_back(ln1[0]);
11689         ln.push_back(ln1[1]);
11690         ln.push_back(ln1[2]);
11691         ln.push_back(ln3[0]);
11692         ln.push_back(ln3[1]);
11693         ln.push_back(ln3[2]);
11694         break;
11695       case VTK_QUADRATIC_QUAD:
11696         //              vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11697         //                                      ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11698         //                                      ln4[0], ln4[1], ln4[2], ln4[3]);
11699         vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11700                                 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11701                                 ln4[0], ln4[1], ln4[2], ln4[3]);
11702         // MESSAGE("vol quad hexa " << vol->GetID());
11703         ln.push_back(ln1[0]);
11704         ln.push_back(ln1[1]);
11705         ln.push_back(ln1[2]);
11706         ln.push_back(ln1[3]);
11707         ln.push_back(ln3[0]);
11708         ln.push_back(ln3[1]);
11709         ln.push_back(ln3[2]);
11710         ln.push_back(ln3[3]);
11711         break;
11712       case VTK_POLYGON:
11713         break;
11714       default:
11715         break;
11716       }
11717
11718       if (vol)
11719       {
11720         stringstream grpname;
11721         grpname << "jf_";
11722         grpname << idom;
11723         int idg;
11724         string namegrp = grpname.str();
11725         if (!mapOfJunctionGroups.count(namegrp))
11726           mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11727         SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11728         if (sgrp)
11729           sgrp->Add(vol->GetID());
11730       }
11731
11732       // --- modify the face
11733
11734       aFace->ChangeNodes(&ln[0], ln.size());
11735     }
11736   }
11737   return true;
11738 }
11739
11740 /*!
11741  *  \brief identify all the elements around a geom shape, get the faces delimiting the hole
11742  *  Build groups of volume to remove, groups of faces to replace on the skin of the object,
11743  *  groups of faces to remove inside the object, (idem edges).
11744  *  Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11745  */
11746 void SMESH_MeshEditor::CreateHoleSkin(double                          radius,
11747                                       const TopoDS_Shape&             theShape,
11748                                       SMESH_NodeSearcher*             theNodeSearcher,
11749                                       const char*                     groupName,
11750                                       std::vector<double>&            nodesCoords,
11751                                       std::vector<std::vector<int> >& listOfListOfNodes)
11752 {
11753   MESSAGE("--------------------------------");
11754   MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11755   MESSAGE("--------------------------------");
11756
11757   // --- zone of volumes to remove is given :
11758   //     1 either by a geom shape (one or more vertices) and a radius,
11759   //     2 either by a group of nodes (representative of the shape)to use with the radius,
11760   //     3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11761   //     In the case 2, the group of nodes is an external group of nodes from another mesh,
11762   //     In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11763   //     defined by it's name.
11764
11765   SMESHDS_GroupBase* groupDS = 0;
11766   SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11767   while ( groupIt->more() )
11768   {
11769     groupDS = 0;
11770     SMESH_Group * group = groupIt->next();
11771     if ( !group ) continue;
11772     groupDS = group->GetGroupDS();
11773     if ( !groupDS || groupDS->IsEmpty() ) continue;
11774     std::string grpName = group->GetName();
11775     //MESSAGE("grpName=" << grpName);
11776     if (grpName == groupName)
11777       break;
11778     else
11779       groupDS = 0;
11780   }
11781
11782   bool isNodeGroup = false;
11783   bool isNodeCoords = false;
11784   if (groupDS)
11785   {
11786     if (groupDS->GetType() != SMDSAbs_Node)
11787       return;
11788     isNodeGroup = true;     // a group of nodes exists and it is in this mesh
11789   }
11790
11791   if (nodesCoords.size() > 0)
11792     isNodeCoords = true; // a list o nodes given by their coordinates
11793   //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11794
11795   // --- define groups to build
11796
11797   int idg; // --- group of SMDS volumes
11798   string grpvName = groupName;
11799   grpvName += "_vol";
11800   SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
11801   if (!grp)
11802   {
11803     MESSAGE("group not created " << grpvName);
11804     return;
11805   }
11806   SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
11807
11808   int idgs; // --- group of SMDS faces on the skin
11809   string grpsName = groupName;
11810   grpsName += "_skin";
11811   SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
11812   if (!grps)
11813   {
11814     MESSAGE("group not created " << grpsName);
11815     return;
11816   }
11817   SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
11818
11819   int idgi; // --- group of SMDS faces internal (several shapes)
11820   string grpiName = groupName;
11821   grpiName += "_internalFaces";
11822   SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
11823   if (!grpi)
11824   {
11825     MESSAGE("group not created " << grpiName);
11826     return;
11827   }
11828   SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
11829
11830   int idgei; // --- group of SMDS faces internal (several shapes)
11831   string grpeiName = groupName;
11832   grpeiName += "_internalEdges";
11833   SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
11834   if (!grpei)
11835   {
11836     MESSAGE("group not created " << grpeiName);
11837     return;
11838   }
11839   SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
11840
11841   // --- build downward connectivity
11842
11843   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11844   meshDS->BuildDownWardConnectivity(true);
11845   SMDS_UnstructuredGrid* grid = meshDS->getGrid();
11846
11847   // --- set of volumes detected inside
11848
11849   std::set<int> setOfInsideVol;
11850   std::set<int> setOfVolToCheck;
11851
11852   std::vector<gp_Pnt> gpnts;
11853   gpnts.clear();
11854
11855   if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
11856   {
11857     MESSAGE("group of nodes provided");
11858     SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
11859     while ( elemIt->more() )
11860     {
11861       const SMDS_MeshElement* elem = elemIt->next();
11862       if (!elem)
11863         continue;
11864       const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
11865       if (!node)
11866         continue;
11867       SMDS_MeshElement* vol = 0;
11868       SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
11869       while (volItr->more())
11870       {
11871         vol = (SMDS_MeshElement*)volItr->next();
11872         setOfInsideVol.insert(vol->getVtkId());
11873         sgrp->Add(vol->GetID());
11874       }
11875     }
11876   }
11877   else if (isNodeCoords)
11878   {
11879     MESSAGE("list of nodes coordinates provided");
11880     size_t i = 0;
11881     int k = 0;
11882     while ( i < nodesCoords.size()-2 )
11883     {
11884       double x = nodesCoords[i++];
11885       double y = nodesCoords[i++];
11886       double z = nodesCoords[i++];
11887       gp_Pnt p = gp_Pnt(x, y ,z);
11888       gpnts.push_back(p);
11889       MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
11890       k++;
11891     }
11892   }
11893   else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
11894   {
11895     MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
11896     TopTools_IndexedMapOfShape vertexMap;
11897     TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
11898     gp_Pnt p = gp_Pnt(0,0,0);
11899     if (vertexMap.Extent() < 1)
11900       return;
11901
11902     for ( int i = 1; i <= vertexMap.Extent(); ++i )
11903     {
11904       const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
11905       p = BRep_Tool::Pnt(vertex);
11906       gpnts.push_back(p);
11907       MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
11908     }
11909   }
11910
11911   if (gpnts.size() > 0)
11912   {
11913     int nodeId = 0;
11914     const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
11915     if (startNode)
11916       nodeId = startNode->GetID();
11917     MESSAGE("nodeId " << nodeId);
11918
11919     double radius2 = radius*radius;
11920     MESSAGE("radius2 " << radius2);
11921
11922     // --- volumes on start node
11923
11924     setOfVolToCheck.clear();
11925     SMDS_MeshElement* startVol = 0;
11926     SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
11927     while (volItr->more())
11928     {
11929       startVol = (SMDS_MeshElement*)volItr->next();
11930       setOfVolToCheck.insert(startVol->getVtkId());
11931     }
11932     if (setOfVolToCheck.empty())
11933     {
11934       MESSAGE("No volumes found");
11935       return;
11936     }
11937
11938     // --- starting with central volumes then their neighbors, check if they are inside
11939     //     or outside the domain, until no more new neighbor volume is inside.
11940     //     Fill the group of inside volumes
11941
11942     std::map<int, double> mapOfNodeDistance2;
11943     mapOfNodeDistance2.clear();
11944     std::set<int> setOfOutsideVol;
11945     while (!setOfVolToCheck.empty())
11946     {
11947       std::set<int>::iterator it = setOfVolToCheck.begin();
11948       int vtkId = *it;
11949       MESSAGE("volume to check,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11950       bool volInside = false;
11951       vtkIdType npts = 0;
11952       vtkIdType* pts = 0;
11953       grid->GetCellPoints(vtkId, npts, pts);
11954       for (int i=0; i<npts; i++)
11955       {
11956         double distance2 = 0;
11957         if (mapOfNodeDistance2.count(pts[i]))
11958         {
11959           distance2 = mapOfNodeDistance2[pts[i]];
11960           MESSAGE("point " << pts[i] << " distance2 " << distance2);
11961         }
11962         else
11963         {
11964           double *coords = grid->GetPoint(pts[i]);
11965           gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
11966           distance2 = 1.E40;
11967           for ( size_t j = 0; j < gpnts.size(); j++ )
11968           {
11969             double d2 = aPoint.SquareDistance( gpnts[ j ]);
11970             if (d2 < distance2)
11971             {
11972               distance2 = d2;
11973               if (distance2 < radius2)
11974                 break;
11975             }
11976           }
11977           mapOfNodeDistance2[pts[i]] = distance2;
11978           MESSAGE("  point "  << pts[i]  << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " <<  coords[2]);
11979         }
11980         if (distance2 < radius2)
11981         {
11982           volInside = true; // one or more nodes inside the domain
11983           sgrp->Add(meshDS->fromVtkToSmds(vtkId));
11984           break;
11985         }
11986       }
11987       if (volInside)
11988       {
11989         setOfInsideVol.insert(vtkId);
11990         MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11991         int neighborsVtkIds[NBMAXNEIGHBORS];
11992         int downIds[NBMAXNEIGHBORS];
11993         unsigned char downTypes[NBMAXNEIGHBORS];
11994         int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11995         for (int n = 0; n < nbNeighbors; n++)
11996           if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
11997             setOfVolToCheck.insert(neighborsVtkIds[n]);
11998       }
11999       else
12000       {
12001         setOfOutsideVol.insert(vtkId);
12002         MESSAGE("  volume outside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12003       }
12004       setOfVolToCheck.erase(vtkId);
12005     }
12006   }
12007
12008   // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12009   //     If yes, add the volume to the inside set
12010
12011   bool addedInside = true;
12012   std::set<int> setOfVolToReCheck;
12013   while (addedInside)
12014   {
12015     MESSAGE(" --------------------------- re check");
12016     addedInside = false;
12017     std::set<int>::iterator itv = setOfInsideVol.begin();
12018     for (; itv != setOfInsideVol.end(); ++itv)
12019     {
12020       int vtkId = *itv;
12021       int neighborsVtkIds[NBMAXNEIGHBORS];
12022       int downIds[NBMAXNEIGHBORS];
12023       unsigned char downTypes[NBMAXNEIGHBORS];
12024       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12025       for (int n = 0; n < nbNeighbors; n++)
12026         if (!setOfInsideVol.count(neighborsVtkIds[n]))
12027           setOfVolToReCheck.insert(neighborsVtkIds[n]);
12028     }
12029     setOfVolToCheck = setOfVolToReCheck;
12030     setOfVolToReCheck.clear();
12031     while  (!setOfVolToCheck.empty())
12032     {
12033       std::set<int>::iterator it = setOfVolToCheck.begin();
12034       int vtkId = *it;
12035       if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12036       {
12037         MESSAGE("volume to recheck,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12038         int countInside = 0;
12039         int neighborsVtkIds[NBMAXNEIGHBORS];
12040         int downIds[NBMAXNEIGHBORS];
12041         unsigned char downTypes[NBMAXNEIGHBORS];
12042         int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12043         for (int n = 0; n < nbNeighbors; n++)
12044           if (setOfInsideVol.count(neighborsVtkIds[n]))
12045             countInside++;
12046         MESSAGE("countInside " << countInside);
12047         if (countInside > 1)
12048         {
12049           MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12050           setOfInsideVol.insert(vtkId);
12051           sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12052           addedInside = true;
12053         }
12054         else
12055           setOfVolToReCheck.insert(vtkId);
12056       }
12057       setOfVolToCheck.erase(vtkId);
12058     }
12059   }
12060
12061   // --- map of Downward faces at the boundary, inside the global volume
12062   //     map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12063   //     fill group of SMDS faces inside the volume (when several volume shapes)
12064   //     fill group of SMDS faces on the skin of the global volume (if skin)
12065
12066   std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12067   std::map<DownIdType, int, DownIdCompare> skinFaces;     // faces on the skin of the global volume --> corresponding cell
12068   std::set<int>::iterator it = setOfInsideVol.begin();
12069   for (; it != setOfInsideVol.end(); ++it)
12070   {
12071     int vtkId = *it;
12072     //MESSAGE("  vtkId " << vtkId  << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12073     int neighborsVtkIds[NBMAXNEIGHBORS];
12074     int downIds[NBMAXNEIGHBORS];
12075     unsigned char downTypes[NBMAXNEIGHBORS];
12076     int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12077     for (int n = 0; n < nbNeighbors; n++)
12078     {
12079       int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12080       if (neighborDim == 3)
12081       {
12082         if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12083         {
12084           DownIdType face(downIds[n], downTypes[n]);
12085           boundaryFaces[face] = vtkId;
12086         }
12087         // if the face between to volumes is in the mesh, get it (internal face between shapes)
12088         int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12089         if (vtkFaceId >= 0)
12090         {
12091           sgrpi->Add(meshDS->fromVtkToSmds(vtkFaceId));
12092           // find also the smds edges on this face
12093           int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12094           const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12095           const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12096           for (int i = 0; i < nbEdges; i++)
12097           {
12098             int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12099             if (vtkEdgeId >= 0)
12100               sgrpei->Add(meshDS->fromVtkToSmds(vtkEdgeId));
12101           }
12102         }
12103       }
12104       else if (neighborDim == 2) // skin of the volume
12105       {
12106         DownIdType face(downIds[n], downTypes[n]);
12107         skinFaces[face] = vtkId;
12108         int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12109         if (vtkFaceId >= 0)
12110           sgrps->Add(meshDS->fromVtkToSmds(vtkFaceId));
12111       }
12112     }
12113   }
12114
12115   // --- identify the edges constituting the wire of each subshape on the skin
12116   //     define polylines with the nodes of edges, equivalent to wires
12117   //     project polylines on subshapes, and partition, to get geom faces
12118
12119   std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12120   std::set<int> emptySet;
12121   emptySet.clear();
12122   std::set<int> shapeIds;
12123
12124   SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12125   while (itelem->more())
12126   {
12127     const SMDS_MeshElement *elem = itelem->next();
12128     int shapeId = elem->getshapeId();
12129     int vtkId = elem->getVtkId();
12130     if (!shapeIdToVtkIdSet.count(shapeId))
12131     {
12132       shapeIdToVtkIdSet[shapeId] = emptySet;
12133       shapeIds.insert(shapeId);
12134     }
12135     shapeIdToVtkIdSet[shapeId].insert(vtkId);
12136   }
12137
12138   std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12139   std::set<DownIdType, DownIdCompare> emptyEdges;
12140   emptyEdges.clear();
12141
12142   std::map<int, std::set<int> >::iterator itShape =  shapeIdToVtkIdSet.begin();
12143   for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12144   {
12145     int shapeId = itShape->first;
12146     MESSAGE(" --- Shape ID --- "<< shapeId);
12147     shapeIdToEdges[shapeId] = emptyEdges;
12148
12149     std::vector<int> nodesEdges;
12150
12151     std::set<int>::iterator its = itShape->second.begin();
12152     for (; its != itShape->second.end(); ++its)
12153     {
12154       int vtkId = *its;
12155       MESSAGE("     " << vtkId);
12156       int neighborsVtkIds[NBMAXNEIGHBORS];
12157       int downIds[NBMAXNEIGHBORS];
12158       unsigned char downTypes[NBMAXNEIGHBORS];
12159       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12160       for (int n = 0; n < nbNeighbors; n++)
12161       {
12162         if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12163           continue;
12164         int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
12165         const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12166         if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12167         {
12168           DownIdType edge(downIds[n], downTypes[n]);
12169           if (!shapeIdToEdges[shapeId].count(edge))
12170           {
12171             shapeIdToEdges[shapeId].insert(edge);
12172             int vtkNodeId[3];
12173             int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12174             nodesEdges.push_back(vtkNodeId[0]);
12175             nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12176             MESSAGE("       --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12177           }
12178         }
12179       }
12180     }
12181
12182     std::list<int> order;
12183     order.clear();
12184     if (nodesEdges.size() > 0)
12185     {
12186       order.push_back(nodesEdges[0]); MESSAGE("       --- back " << order.back()+1); // SMDS id = VTK id + 1;
12187       nodesEdges[0] = -1;
12188       order.push_back(nodesEdges[1]); MESSAGE("       --- back " << order.back()+1);
12189       nodesEdges[1] = -1; // do not reuse this edge
12190       bool found = true;
12191       while (found)
12192       {
12193         int nodeTofind = order.back(); // try first to push back
12194         int i = 0;
12195         for ( i = 0; i < (int)nodesEdges.size(); i++ )
12196           if (nodesEdges[i] == nodeTofind)
12197             break;
12198         if ( i == (int) nodesEdges.size() )
12199           found = false; // no follower found on back
12200         else
12201         {
12202           if (i%2) // odd ==> use the previous one
12203             if (nodesEdges[i-1] < 0)
12204               found = false;
12205             else
12206             {
12207               order.push_back(nodesEdges[i-1]); MESSAGE("       --- back " << order.back()+1);
12208               nodesEdges[i-1] = -1;
12209             }
12210           else // even ==> use the next one
12211             if (nodesEdges[i+1] < 0)
12212               found = false;
12213             else
12214             {
12215               order.push_back(nodesEdges[i+1]); MESSAGE("       --- back " << order.back()+1);
12216               nodesEdges[i+1] = -1;
12217             }
12218         }
12219         if (found)
12220           continue;
12221         // try to push front
12222         found = true;
12223         nodeTofind = order.front(); // try to push front
12224         for ( i = 0;  i < (int)nodesEdges.size(); i++ )
12225           if ( nodesEdges[i] == nodeTofind )
12226             break;
12227         if ( i == (int)nodesEdges.size() )
12228         {
12229           found = false; // no predecessor found on front
12230           continue;
12231         }
12232         if (i%2) // odd ==> use the previous one
12233           if (nodesEdges[i-1] < 0)
12234             found = false;
12235           else
12236           {
12237             order.push_front(nodesEdges[i-1]); MESSAGE("       --- front " << order.front()+1);
12238             nodesEdges[i-1] = -1;
12239           }
12240         else // even ==> use the next one
12241           if (nodesEdges[i+1] < 0)
12242             found = false;
12243           else
12244           {
12245             order.push_front(nodesEdges[i+1]); MESSAGE("       --- front " << order.front()+1);
12246             nodesEdges[i+1] = -1;
12247           }
12248       }
12249     }
12250
12251
12252     std::vector<int> nodes;
12253     nodes.push_back(shapeId);
12254     std::list<int>::iterator itl = order.begin();
12255     for (; itl != order.end(); itl++)
12256     {
12257       nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12258       MESSAGE("              ordered node " << nodes[nodes.size()-1]);
12259     }
12260     listOfListOfNodes.push_back(nodes);
12261   }
12262
12263   //     partition geom faces with blocFissure
12264   //     mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12265   //     mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12266
12267   return;
12268 }
12269
12270
12271 //================================================================================
12272 /*!
12273  * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12274  * The created 2D mesh elements based on nodes of free faces of boundary volumes
12275  * \return TRUE if operation has been completed successfully, FALSE otherwise
12276  */
12277 //================================================================================
12278
12279 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12280 {
12281   // iterates on volume elements and detect all free faces on them
12282   SMESHDS_Mesh* aMesh = GetMeshDS();
12283   if (!aMesh)
12284     return false;
12285
12286   ElemFeatures faceType( SMDSAbs_Face );
12287   int nbFree = 0, nbExisted = 0, nbCreated = 0;
12288   SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12289   while(vIt->more())
12290   {
12291     const SMDS_MeshVolume* volume = vIt->next();
12292     SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12293     vTool.SetExternalNormal();
12294     const int iQuad = volume->IsQuadratic();
12295     faceType.SetQuad( iQuad );
12296     for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12297     {
12298       if (!vTool.IsFreeFace(iface))
12299         continue;
12300       nbFree++;
12301       vector<const SMDS_MeshNode *> nodes;
12302       int nbFaceNodes = vTool.NbFaceNodes(iface);
12303       const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12304       int inode = 0;
12305       for ( ; inode < nbFaceNodes; inode += iQuad+1)
12306         nodes.push_back(faceNodes[inode]);
12307
12308       if (iQuad) // add medium nodes
12309       {
12310         for ( inode = 1; inode < nbFaceNodes; inode += 2)
12311           nodes.push_back(faceNodes[inode]);
12312         if ( nbFaceNodes == 9 ) // bi-quadratic quad
12313           nodes.push_back(faceNodes[8]);
12314       }
12315       // add new face based on volume nodes
12316       if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) )
12317       {
12318         nbExisted++; // face already exsist
12319       }
12320       else
12321       {
12322         AddElement( nodes, faceType.SetPoly( nbFaceNodes/(iQuad+1) > 4 ));
12323         nbCreated++;
12324       }
12325     }
12326   }
12327   return ( nbFree == ( nbExisted + nbCreated ));
12328 }
12329
12330 namespace
12331 {
12332   inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12333   {
12334     if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12335       return n;
12336     return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12337   }
12338 }
12339 //================================================================================
12340 /*!
12341  * \brief Creates missing boundary elements
12342  *  \param elements - elements whose boundary is to be checked
12343  *  \param dimension - defines type of boundary elements to create
12344  *  \param group - a group to store created boundary elements in
12345  *  \param targetMesh - a mesh to store created boundary elements in
12346  *  \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12347  *  \param toCopyExistingBoundary - if true, not only new but also pre-existing
12348  *                                boundary elements will be copied into the targetMesh
12349  *  \param toAddExistingBondary - if true, not only new but also pre-existing
12350  *                                boundary elements will be added into the new group
12351  *  \param aroundElements - if true, elements will be created on boundary of given
12352  *                          elements else, on boundary of the whole mesh.
12353  * \return nb of added boundary elements
12354  */
12355 //================================================================================
12356
12357 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12358                                        Bnd_Dimension           dimension,
12359                                        SMESH_Group*            group/*=0*/,
12360                                        SMESH_Mesh*             targetMesh/*=0*/,
12361                                        bool                    toCopyElements/*=false*/,
12362                                        bool                    toCopyExistingBoundary/*=false*/,
12363                                        bool                    toAddExistingBondary/*= false*/,
12364                                        bool                    aroundElements/*= false*/)
12365 {
12366   SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12367   SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12368   // hope that all elements are of the same type, do not check them all
12369   if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12370     throw SALOME_Exception(LOCALIZED("wrong element type"));
12371
12372   if ( !targetMesh )
12373     toCopyElements = toCopyExistingBoundary = false;
12374
12375   SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12376   SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12377   int nbAddedBnd = 0;
12378
12379   // editor adding present bnd elements and optionally holding elements to add to the group
12380   SMESH_MeshEditor* presentEditor;
12381   SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12382   presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12383
12384   SMESH_MesherHelper helper( *myMesh );
12385   const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12386   SMDS_VolumeTool vTool;
12387   TIDSortedElemSet avoidSet;
12388   const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12389   size_t inode;
12390
12391   typedef vector<const SMDS_MeshNode*> TConnectivity;
12392   TConnectivity tgtNodes;
12393   ElemFeatures elemKind( missType ), elemToCopy;
12394
12395   vector<const SMDS_MeshElement*> presentBndElems;
12396   vector<TConnectivity>           missingBndElems;
12397   vector<int>                     freeFacets;
12398   TConnectivity nodes, elemNodes;
12399
12400   SMDS_ElemIteratorPtr eIt;
12401   if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12402   else                  eIt = elemSetIterator( elements );
12403
12404   while (eIt->more())
12405   {
12406     const SMDS_MeshElement* elem = eIt->next();
12407     const int              iQuad = elem->IsQuadratic();
12408     elemKind.SetQuad( iQuad );
12409
12410     // ------------------------------------------------------------------------------------
12411     // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12412     // ------------------------------------------------------------------------------------
12413     presentBndElems.clear();
12414     missingBndElems.clear();
12415     freeFacets.clear(); nodes.clear(); elemNodes.clear();
12416     if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12417     {
12418       const SMDS_MeshElement* otherVol = 0;
12419       for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12420       {
12421         if ( !vTool.IsFreeFace(iface, &otherVol) &&
12422              ( !aroundElements || elements.count( otherVol )))
12423           continue;
12424         freeFacets.push_back( iface );
12425       }
12426       if ( missType == SMDSAbs_Face )
12427         vTool.SetExternalNormal();
12428       for ( size_t i = 0; i < freeFacets.size(); ++i )
12429       {
12430         int                iface = freeFacets[i];
12431         const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12432         const size_t nbFaceNodes = vTool.NbFaceNodes (iface);
12433         if ( missType == SMDSAbs_Edge ) // boundary edges
12434         {
12435           nodes.resize( 2+iQuad );
12436           for ( size_t i = 0; i < nbFaceNodes; i += 1+iQuad )
12437           {
12438             for ( size_t j = 0; j < nodes.size(); ++j )
12439               nodes[ j ] = nn[ i+j ];
12440             if ( const SMDS_MeshElement* edge =
12441                  aMesh->FindElement( nodes, SMDSAbs_Edge, /*noMedium=*/false ))
12442               presentBndElems.push_back( edge );
12443             else
12444               missingBndElems.push_back( nodes );
12445           }
12446         }
12447         else // boundary face
12448         {
12449           nodes.clear();
12450           for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12451             nodes.push_back( nn[inode] ); // add corner nodes
12452           if (iQuad)
12453             for ( inode = 1; inode < nbFaceNodes; inode += 2)
12454               nodes.push_back( nn[inode] ); // add medium nodes
12455           int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12456           if ( iCenter > 0 )
12457             nodes.push_back( vTool.GetNodes()[ iCenter ] );
12458
12459           if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12460                                                                SMDSAbs_Face, /*noMedium=*/false ))
12461             presentBndElems.push_back( f );
12462           else
12463             missingBndElems.push_back( nodes );
12464
12465           if ( targetMesh != myMesh )
12466           {
12467             // add 1D elements on face boundary to be added to a new mesh
12468             const SMDS_MeshElement* edge;
12469             for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12470             {
12471               if ( iQuad )
12472                 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12473               else
12474                 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12475               if ( edge && avoidSet.insert( edge ).second )
12476                 presentBndElems.push_back( edge );
12477             }
12478           }
12479         }
12480       }
12481     }
12482     else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12483     {
12484       avoidSet.clear(), avoidSet.insert( elem );
12485       elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesElemIterator() ),
12486                         SMDS_MeshElement::iterator() );
12487       elemNodes.push_back( elemNodes[0] );
12488       nodes.resize( 2 + iQuad );
12489       const int nbLinks = elem->NbCornerNodes();
12490       for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12491       {
12492         nodes[0] = elemNodes[iN];
12493         nodes[1] = elemNodes[iN+1+iQuad];
12494         if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12495           continue; // not free link
12496
12497         if ( iQuad ) nodes[2] = elemNodes[iN+1];
12498         if ( const SMDS_MeshElement* edge =
12499              aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12500           presentBndElems.push_back( edge );
12501         else
12502           missingBndElems.push_back( nodes );
12503       }
12504     }
12505
12506     // ---------------------------------
12507     // 2. Add missing boundary elements
12508     // ---------------------------------
12509     if ( targetMesh != myMesh )
12510       // instead of making a map of nodes in this mesh and targetMesh,
12511       // we create nodes with same IDs.
12512       for ( size_t i = 0; i < missingBndElems.size(); ++i )
12513       {
12514         TConnectivity& srcNodes = missingBndElems[i];
12515         tgtNodes.resize( srcNodes.size() );
12516         for ( inode = 0; inode < srcNodes.size(); ++inode )
12517           tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12518         if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( tgtNodes,
12519                                                                    missType,
12520                                                                    /*noMedium=*/false))
12521           continue;
12522         tgtEditor.AddElement( tgtNodes, elemKind.SetPoly( tgtNodes.size()/(iQuad+1) > 4 ));
12523         ++nbAddedBnd;
12524       }
12525     else
12526       for ( size_t i = 0; i < missingBndElems.size(); ++i )
12527       {
12528         TConnectivity& nodes = missingBndElems[ i ];
12529         if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12530                                                                    missType,
12531                                                                    /*noMedium=*/false))
12532           continue;
12533         SMDS_MeshElement* newElem =
12534           tgtEditor.AddElement( nodes, elemKind.SetPoly( nodes.size()/(iQuad+1) > 4 ));
12535         nbAddedBnd += bool( newElem );
12536
12537         // try to set a new element to a shape
12538         if ( myMesh->HasShapeToMesh() )
12539         {
12540           bool ok = true;
12541           set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12542           const size_t nbN = nodes.size() / (iQuad+1 );
12543           for ( inode = 0; inode < nbN && ok; ++inode )
12544           {
12545             pair<int, TopAbs_ShapeEnum> i_stype =
12546               helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12547             if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12548               mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12549           }
12550           if ( ok && mediumShapes.size() > 1 )
12551           {
12552             set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12553             pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12554             for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12555             {
12556               if (( ok = ( stype_i->first != stype_i_0.first )))
12557                 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12558                                         aMesh->IndexToShape( stype_i_0.second ));
12559             }
12560           }
12561           if ( ok && mediumShapes.begin()->first == missShapeType )
12562             aMesh->SetMeshElementOnShape( newElem, mediumShapes.begin()->second );
12563         }
12564       }
12565
12566     // ----------------------------------
12567     // 3. Copy present boundary elements
12568     // ----------------------------------
12569     if ( toCopyExistingBoundary )
12570       for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12571       {
12572         const SMDS_MeshElement* e = presentBndElems[i];
12573         tgtNodes.resize( e->NbNodes() );
12574         for ( inode = 0; inode < tgtNodes.size(); ++inode )
12575           tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12576         presentEditor->AddElement( tgtNodes, elemToCopy.Init( e ));
12577       }
12578     else // store present elements to add them to a group
12579       for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12580       {
12581         presentEditor->myLastCreatedElems.Append( presentBndElems[ i ]);
12582       }
12583
12584   } // loop on given elements
12585
12586   // ---------------------------------------------
12587   // 4. Fill group with boundary elements
12588   // ---------------------------------------------
12589   if ( group )
12590   {
12591     if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12592       for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i )
12593         g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 ));
12594   }
12595   tgtEditor.myLastCreatedElems.Clear();
12596   tgtEditor2.myLastCreatedElems.Clear();
12597
12598   // -----------------------
12599   // 5. Copy given elements
12600   // -----------------------
12601   if ( toCopyElements && targetMesh != myMesh )
12602   {
12603     if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12604     else                  eIt = elemSetIterator( elements );
12605     while (eIt->more())
12606     {
12607       const SMDS_MeshElement* elem = eIt->next();
12608       tgtNodes.resize( elem->NbNodes() );
12609       for ( inode = 0; inode < tgtNodes.size(); ++inode )
12610         tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12611       tgtEditor.AddElement( tgtNodes, elemToCopy.Init( elem ));
12612
12613       tgtEditor.myLastCreatedElems.Clear();
12614     }
12615   }
12616   return nbAddedBnd;
12617 }
12618
12619 //================================================================================
12620 /*!
12621  * \brief Copy node position and set \a to node on the same geometry
12622  */
12623 //================================================================================
12624
12625 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
12626                                      const SMDS_MeshNode* to )
12627 {
12628   if ( !from || !to ) return;
12629
12630   SMDS_PositionPtr pos = from->GetPosition();
12631   if ( !pos || from->getshapeId() < 1 ) return;
12632
12633   switch ( pos->GetTypeOfPosition() )
12634   {
12635   case SMDS_TOP_3DSPACE: break;
12636
12637   case SMDS_TOP_FACE:
12638   {
12639     const SMDS_FacePosition* fPos = static_cast< const SMDS_FacePosition* >( pos );
12640     GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
12641                                 fPos->GetUParameter(), fPos->GetVParameter() );
12642     break;
12643   }
12644   case SMDS_TOP_EDGE:
12645   {
12646     // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
12647     const SMDS_EdgePosition* ePos = static_cast< const SMDS_EdgePosition* >( pos );
12648     GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
12649     break;
12650   }
12651   case SMDS_TOP_VERTEX:
12652   {
12653     GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
12654     break;
12655   }
12656   case SMDS_TOP_UNSPEC:
12657   default:;
12658   }
12659 }