Salome HOME
fix biquadratic quadratic pentahedron(18 nodes prism)
[modules/smesh.git] / src / SMESH / SMESH_MeshEditor.cxx
1 // Copyright (C) 2007-2016  CEA/DEN, EDF R&D, OPEN CASCADE
2 //
3 // Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
5 //
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License, or (at your option) any later version.
10 //
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 // Lesser General Public License for more details.
15 //
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
19 //
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
21 //
22
23 // File      : SMESH_MeshEditor.cxx
24 // Created   : Mon Apr 12 16:10:22 2004
25 // Author    : Edward AGAPOV (eap)
26
27 #include "SMESH_MeshEditor.hxx"
28
29 #include "SMDS_Downward.hxx"
30 #include "SMDS_EdgePosition.hxx"
31 #include "SMDS_FaceOfNodes.hxx"
32 #include "SMDS_FacePosition.hxx"
33 #include "SMDS_LinearEdge.hxx"
34 #include "SMDS_MeshGroup.hxx"
35 #include "SMDS_SetIterator.hxx"
36 #include "SMDS_SpacePosition.hxx"
37 #include "SMDS_VolumeTool.hxx"
38 #include "SMESHDS_Group.hxx"
39 #include "SMESHDS_Mesh.hxx"
40 #include "SMESH_Algo.hxx"
41 #include "SMESH_ControlsDef.hxx"
42 #include "SMESH_Group.hxx"
43 #include "SMESH_Mesh.hxx"
44 #include "SMESH_MeshAlgos.hxx"
45 #include "SMESH_MesherHelper.hxx"
46 #include "SMESH_OctreeNode.hxx"
47 #include "SMESH_subMesh.hxx"
48
49 #include <Basics_OCCTVersion.hxx>
50
51 #include "utilities.h"
52 #include "chrono.hxx"
53
54 #include <BRepAdaptor_Surface.hxx>
55 #include <BRepBuilderAPI_MakeEdge.hxx>
56 #include <BRepClass3d_SolidClassifier.hxx>
57 #include <BRep_Tool.hxx>
58 #include <ElCLib.hxx>
59 #include <Extrema_GenExtPS.hxx>
60 #include <Extrema_POnCurv.hxx>
61 #include <Extrema_POnSurf.hxx>
62 #include <Geom2d_Curve.hxx>
63 #include <GeomAdaptor_Surface.hxx>
64 #include <Geom_Curve.hxx>
65 #include <Geom_Surface.hxx>
66 #include <Precision.hxx>
67 #include <TColStd_ListOfInteger.hxx>
68 #include <TopAbs_State.hxx>
69 #include <TopExp.hxx>
70 #include <TopExp_Explorer.hxx>
71 #include <TopTools_ListIteratorOfListOfShape.hxx>
72 #include <TopTools_ListOfShape.hxx>
73 #include <TopTools_SequenceOfShape.hxx>
74 #include <TopoDS.hxx>
75 #include <TopoDS_Edge.hxx>
76 #include <TopoDS_Face.hxx>
77 #include <TopoDS_Solid.hxx>
78 #include <gp.hxx>
79 #include <gp_Ax1.hxx>
80 #include <gp_Dir.hxx>
81 #include <gp_Lin.hxx>
82 #include <gp_Pln.hxx>
83 #include <gp_Trsf.hxx>
84 #include <gp_Vec.hxx>
85 #include <gp_XY.hxx>
86 #include <gp_XYZ.hxx>
87
88 #include <cmath>
89
90 #include <map>
91 #include <set>
92 #include <numeric>
93 #include <limits>
94 #include <algorithm>
95 #include <sstream>
96
97 #include <boost/tuple/tuple.hpp>
98
99 #include <Standard_Failure.hxx>
100 #include <Standard_ErrorHandler.hxx>
101
102 #define cast2Node(elem) static_cast<const SMDS_MeshNode*>( elem )
103
104 using namespace std;
105 using namespace SMESH::Controls;
106
107 namespace
108 {
109   template < class ELEM_SET >
110   SMDS_ElemIteratorPtr elemSetIterator( const ELEM_SET& elements )
111   {
112     typedef SMDS_SetIterator
113       < SMDS_pElement, typename ELEM_SET::const_iterator> TSetIterator;
114     return SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
115   }
116 }
117
118 //=======================================================================
119 //function : SMESH_MeshEditor
120 //purpose  :
121 //=======================================================================
122
123 SMESH_MeshEditor::SMESH_MeshEditor( SMESH_Mesh* theMesh )
124   :myMesh( theMesh ) // theMesh may be NULL
125 {
126 }
127
128 //================================================================================
129 /*!
130  * \brief Return mesh DS
131  */
132 //================================================================================
133
134 SMESHDS_Mesh * SMESH_MeshEditor::GetMeshDS()
135 {
136   return myMesh->GetMeshDS();
137 }
138
139
140 //================================================================================
141 /*!
142  * \brief Clears myLastCreatedNodes and myLastCreatedElems
143  */
144 //================================================================================
145
146 void SMESH_MeshEditor::ClearLastCreated()
147 {
148   myLastCreatedNodes.Clear();
149   myLastCreatedElems.Clear();
150 }
151
152 //================================================================================
153 /*!
154  * \brief Initializes members by an existing element
155  *  \param [in] elem - the source element
156  *  \param [in] basicOnly - if true, does not set additional data of Ball and Polyhedron
157  */
158 //================================================================================
159
160 SMESH_MeshEditor::ElemFeatures&
161 SMESH_MeshEditor::ElemFeatures::Init( const SMDS_MeshElement* elem, bool basicOnly )
162 {
163   if ( elem )
164   {
165     myType = elem->GetType();
166     if ( myType == SMDSAbs_Face || myType == SMDSAbs_Volume )
167     {
168       myIsPoly = elem->IsPoly();
169       if ( myIsPoly )
170       {
171         myIsQuad = elem->IsQuadratic();
172         if ( myType == SMDSAbs_Volume && !basicOnly )
173         {
174           vector<int > quant = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
175           myPolyhedQuantities.swap( quant );
176         }
177       }
178     }
179     else if ( myType == SMDSAbs_Ball && !basicOnly )
180     {
181       myBallDiameter = static_cast<const SMDS_BallElement*>(elem)->GetDiameter();
182     }
183   }
184   return *this;
185 }
186
187 //=======================================================================
188 /*!
189  * \brief Add element
190  */
191 //=======================================================================
192
193 SMDS_MeshElement*
194 SMESH_MeshEditor::AddElement(const vector<const SMDS_MeshNode*> & node,
195                              const ElemFeatures&                  features)
196 {
197   SMDS_MeshElement* e = 0;
198   int nbnode = node.size();
199   SMESHDS_Mesh* mesh = GetMeshDS();
200   const int ID = features.myID;
201
202   switch ( features.myType ) {
203   case SMDSAbs_Face:
204     if ( !features.myIsPoly ) {
205       if      (nbnode == 3) {
206         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], ID);
207         else           e = mesh->AddFace      (node[0], node[1], node[2] );
208       }
209       else if (nbnode == 4) {
210         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], ID);
211         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3] );
212       }
213       else if (nbnode == 6) {
214         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
215                                                node[4], node[5], ID);
216         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
217                                                node[4], node[5] );
218       }
219       else if (nbnode == 7) {
220         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
221                                                node[4], node[5], node[6], ID);
222         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
223                                                node[4], node[5], node[6] );
224       }
225       else if (nbnode == 8) {
226         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
227                                                node[4], node[5], node[6], node[7], ID);
228         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
229                                                node[4], node[5], node[6], node[7] );
230       }
231       else if (nbnode == 9) {
232         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
233                                                node[4], node[5], node[6], node[7], node[8], ID);
234         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
235                                                node[4], node[5], node[6], node[7], node[8] );
236       }
237     }
238     else if ( !features.myIsQuad )
239     {
240       if ( ID >= 1 ) e = mesh->AddPolygonalFaceWithID(node, ID);
241       else           e = mesh->AddPolygonalFace      (node    );
242     }
243     else if ( nbnode % 2 == 0 ) // just a protection
244     {
245       if ( ID >= 1 ) e = mesh->AddQuadPolygonalFaceWithID(node, ID);
246       else           e = mesh->AddQuadPolygonalFace      (node    );
247     }
248     break;
249
250   case SMDSAbs_Volume:
251     if ( !features.myIsPoly ) {
252       if      (nbnode == 4) {
253         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], ID);
254         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3] );
255       }
256       else if (nbnode == 5) {
257         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
258                                                  node[4], ID);
259         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
260                                                  node[4] );
261       }
262       else if (nbnode == 6) {
263         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
264                                                  node[4], node[5], ID);
265         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
266                                                  node[4], node[5] );
267       }
268       else if (nbnode == 8) {
269         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
270                                                  node[4], node[5], node[6], node[7], ID);
271         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
272                                                  node[4], node[5], node[6], node[7] );
273       }
274       else if (nbnode == 10) {
275         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
276                                                  node[4], node[5], node[6], node[7],
277                                                  node[8], node[9], ID);
278         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
279                                                  node[4], node[5], node[6], node[7],
280                                                  node[8], node[9] );
281       }
282       else if (nbnode == 12) {
283         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
284                                                  node[4], node[5], node[6], node[7],
285                                                  node[8], node[9], node[10], node[11], ID);
286         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
287                                                  node[4], node[5], node[6], node[7],
288                                                  node[8], node[9], node[10], node[11] );
289       }
290       else if (nbnode == 13) {
291         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
292                                                  node[4], node[5], node[6], node[7],
293                                                  node[8], node[9], node[10],node[11],
294                                                  node[12],ID);
295         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
296                                                  node[4], node[5], node[6], node[7],
297                                                  node[8], node[9], node[10],node[11],
298                                                  node[12] );
299       }
300       else if (nbnode == 15) {
301         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
302                                                  node[4], node[5], node[6], node[7],
303                                                  node[8], node[9], node[10],node[11],
304                                                  node[12],node[13],node[14],ID);
305         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
306                                                  node[4], node[5], node[6], node[7],
307                                                  node[8], node[9], node[10],node[11],
308                                                  node[12],node[13],node[14] );
309       }
310       else if (nbnode == 20) {
311         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
312                                                  node[4], node[5], node[6], node[7],
313                                                  node[8], node[9], node[10],node[11],
314                                                  node[12],node[13],node[14],node[15],
315                                                  node[16],node[17],node[18],node[19],ID);
316         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
317                                                  node[4], node[5], node[6], node[7],
318                                                  node[8], node[9], node[10],node[11],
319                                                  node[12],node[13],node[14],node[15],
320                                                  node[16],node[17],node[18],node[19] );
321       }
322       else if (nbnode == 27) {
323         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
324                                                  node[4], node[5], node[6], node[7],
325                                                  node[8], node[9], node[10],node[11],
326                                                  node[12],node[13],node[14],node[15],
327                                                  node[16],node[17],node[18],node[19],
328                                                  node[20],node[21],node[22],node[23],
329                                                  node[24],node[25],node[26], ID);
330         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
331                                                  node[4], node[5], node[6], node[7],
332                                                  node[8], node[9], node[10],node[11],
333                                                  node[12],node[13],node[14],node[15],
334                                                  node[16],node[17],node[18],node[19],
335                                                  node[20],node[21],node[22],node[23],
336                                                  node[24],node[25],node[26] );
337       }
338     }
339     else if ( !features.myIsQuad )
340     {
341       if ( ID >= 1 ) e = mesh->AddPolyhedralVolumeWithID(node, features.myPolyhedQuantities, ID);
342       else           e = mesh->AddPolyhedralVolume      (node, features.myPolyhedQuantities    );
343     }
344     else
345     {
346       // if ( ID >= 1 ) e = mesh->AddQuadPolyhedralVolumeWithID(node, features.myPolyhedQuantities,ID);
347       // else           e = mesh->AddQuadPolyhedralVolume      (node, features.myPolyhedQuantities   );
348     }
349     break;
350
351   case SMDSAbs_Edge:
352     if ( nbnode == 2 ) {
353       if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], ID);
354       else           e = mesh->AddEdge      (node[0], node[1] );
355     }
356     else if ( nbnode == 3 ) {
357       if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], node[2], ID);
358       else           e = mesh->AddEdge      (node[0], node[1], node[2] );
359     }
360     break;
361
362   case SMDSAbs_0DElement:
363     if ( nbnode == 1 ) {
364       if ( ID >= 1 ) e = mesh->Add0DElementWithID(node[0], ID);
365       else           e = mesh->Add0DElement      (node[0] );
366     }
367     break;
368
369   case SMDSAbs_Node:
370     if ( ID >= 1 ) e = mesh->AddNodeWithID(node[0]->X(), node[0]->Y(), node[0]->Z(), ID);
371     else           e = mesh->AddNode      (node[0]->X(), node[0]->Y(), node[0]->Z()    );
372     break;
373
374   case SMDSAbs_Ball:
375     if ( ID >= 1 ) e = mesh->AddBallWithID(node[0], features.myBallDiameter, ID);
376     else           e = mesh->AddBall      (node[0], features.myBallDiameter    );
377     break;
378
379   default:;
380   }
381   if ( e ) myLastCreatedElems.Append( e );
382   return e;
383 }
384
385 //=======================================================================
386 /*!
387  * \brief Add element
388  */
389 //=======================================================================
390
391 SMDS_MeshElement* SMESH_MeshEditor::AddElement(const vector<int> & nodeIDs,
392                                                const ElemFeatures& features)
393 {
394   vector<const SMDS_MeshNode*> nodes;
395   nodes.reserve( nodeIDs.size() );
396   vector<int>::const_iterator id = nodeIDs.begin();
397   while ( id != nodeIDs.end() ) {
398     if ( const SMDS_MeshNode* node = GetMeshDS()->FindNode( *id++ ))
399       nodes.push_back( node );
400     else
401       return 0;
402   }
403   return AddElement( nodes, features );
404 }
405
406 //=======================================================================
407 //function : Remove
408 //purpose  : Remove a node or an element.
409 //           Modify a compute state of sub-meshes which become empty
410 //=======================================================================
411
412 int SMESH_MeshEditor::Remove (const list< int >& theIDs,
413                               const bool         isNodes )
414 {
415   myLastCreatedElems.Clear();
416   myLastCreatedNodes.Clear();
417
418   SMESHDS_Mesh* aMesh = GetMeshDS();
419   set< SMESH_subMesh *> smmap;
420
421   int removed = 0;
422   list<int>::const_iterator it = theIDs.begin();
423   for ( ; it != theIDs.end(); it++ ) {
424     const SMDS_MeshElement * elem;
425     if ( isNodes )
426       elem = aMesh->FindNode( *it );
427     else
428       elem = aMesh->FindElement( *it );
429     if ( !elem )
430       continue;
431
432     // Notify VERTEX sub-meshes about modification
433     if ( isNodes ) {
434       const SMDS_MeshNode* node = cast2Node( elem );
435       if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX )
436         if ( int aShapeID = node->getshapeId() )
437           if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
438             smmap.insert( sm );
439     }
440     // Find sub-meshes to notify about modification
441     //     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
442     //     while ( nodeIt->more() ) {
443     //       const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
444     //       const SMDS_PositionPtr& aPosition = node->GetPosition();
445     //       if ( aPosition.get() ) {
446     //         if ( int aShapeID = aPosition->GetShapeId() ) {
447     //           if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
448     //             smmap.insert( sm );
449     //         }
450     //       }
451     //     }
452
453     // Do remove
454     if ( isNodes )
455       aMesh->RemoveNode( static_cast< const SMDS_MeshNode* >( elem ));
456     else
457       aMesh->RemoveElement( elem );
458     removed++;
459   }
460
461   // Notify sub-meshes about modification
462   if ( !smmap.empty() ) {
463     set< SMESH_subMesh *>::iterator smIt;
464     for ( smIt = smmap.begin(); smIt != smmap.end(); smIt++ )
465       (*smIt)->ComputeStateEngine( SMESH_subMesh::MESH_ENTITY_REMOVED );
466   }
467
468   //   // Check if the whole mesh becomes empty
469   //   if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( 1 ) )
470   //     sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
471
472   return removed;
473 }
474
475 //================================================================================
476 /*!
477  * \brief Create 0D elements on all nodes of the given object.
478  *  \param elements - Elements on whose nodes to create 0D elements; if empty, 
479  *                    the all mesh is treated
480  *  \param all0DElems - returns all 0D elements found or created on nodes of \a elements
481  *  \param duplicateElements - to add one more 0D element to a node or not
482  */
483 //================================================================================
484
485 void SMESH_MeshEditor::Create0DElementsOnAllNodes( const TIDSortedElemSet& elements,
486                                                    TIDSortedElemSet&       all0DElems,
487                                                    const bool              duplicateElements )
488 {
489   SMDS_ElemIteratorPtr elemIt;
490   if ( elements.empty() )
491   {
492     elemIt = GetMeshDS()->elementsIterator( SMDSAbs_Node );
493   }
494   else
495   {
496     elemIt = elemSetIterator( elements );
497   }
498
499   while ( elemIt->more() )
500   {
501     const SMDS_MeshElement* e = elemIt->next();
502     SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
503     while ( nodeIt->more() )
504     {
505       const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
506       SMDS_ElemIteratorPtr it0D = n->GetInverseElementIterator( SMDSAbs_0DElement );
507       if ( duplicateElements || !it0D->more() )
508       {
509         myLastCreatedElems.Append( GetMeshDS()->Add0DElement( n ));
510         all0DElems.insert( myLastCreatedElems.Last() );
511       }
512       while ( it0D->more() )
513         all0DElems.insert( it0D->next() );
514     }
515   }
516 }
517
518 //=======================================================================
519 //function : FindShape
520 //purpose  : Return an index of the shape theElem is on
521 //           or zero if a shape not found
522 //=======================================================================
523
524 int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem)
525 {
526   myLastCreatedElems.Clear();
527   myLastCreatedNodes.Clear();
528
529   SMESHDS_Mesh * aMesh = GetMeshDS();
530   if ( aMesh->ShapeToMesh().IsNull() )
531     return 0;
532
533   int aShapeID = theElem->getshapeId();
534   if ( aShapeID < 1 )
535     return 0;
536
537   if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ))
538     if ( sm->Contains( theElem ))
539       return aShapeID;
540
541   if ( theElem->GetType() == SMDSAbs_Node ) {
542     MESSAGE( ":( Error: invalid myShapeId of node " << theElem->GetID() );
543   }
544   else {
545     MESSAGE( ":( Error: invalid myShapeId of element " << theElem->GetID() );
546   }
547
548   TopoDS_Shape aShape; // the shape a node of theElem is on
549   if ( theElem->GetType() != SMDSAbs_Node )
550   {
551     SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
552     while ( nodeIt->more() ) {
553       const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
554       if ((aShapeID = node->getshapeId()) > 0) {
555         if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ) ) {
556           if ( sm->Contains( theElem ))
557             return aShapeID;
558           if ( aShape.IsNull() )
559             aShape = aMesh->IndexToShape( aShapeID );
560         }
561       }
562     }
563   }
564
565   // None of nodes is on a proper shape,
566   // find the shape among ancestors of aShape on which a node is
567   if ( !aShape.IsNull() ) {
568     TopTools_ListIteratorOfListOfShape ancIt( GetMesh()->GetAncestors( aShape ));
569     for ( ; ancIt.More(); ancIt.Next() ) {
570       SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() );
571       if ( sm && sm->Contains( theElem ))
572         return aMesh->ShapeToIndex( ancIt.Value() );
573     }
574   }
575   else
576   {
577     SMESHDS_SubMeshIteratorPtr smIt = GetMeshDS()->SubMeshes();
578     while ( const SMESHDS_SubMesh* sm = smIt->next() )
579       if ( sm->Contains( theElem ))
580         return sm->GetID();
581   }
582
583   return 0;
584 }
585
586 //=======================================================================
587 //function : IsMedium
588 //purpose  :
589 //=======================================================================
590
591 bool SMESH_MeshEditor::IsMedium(const SMDS_MeshNode*      node,
592                                 const SMDSAbs_ElementType typeToCheck)
593 {
594   bool isMedium = false;
595   SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(typeToCheck);
596   while (it->more() && !isMedium ) {
597     const SMDS_MeshElement* elem = it->next();
598     isMedium = elem->IsMediumNode(node);
599   }
600   return isMedium;
601 }
602
603 //=======================================================================
604 //function : shiftNodesQuadTria
605 //purpose  : Shift nodes in the array corresponded to quadratic triangle
606 //           example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
607 //=======================================================================
608
609 static void shiftNodesQuadTria(vector< const SMDS_MeshNode* >& aNodes)
610 {
611   const SMDS_MeshNode* nd1 = aNodes[0];
612   aNodes[0] = aNodes[1];
613   aNodes[1] = aNodes[2];
614   aNodes[2] = nd1;
615   const SMDS_MeshNode* nd2 = aNodes[3];
616   aNodes[3] = aNodes[4];
617   aNodes[4] = aNodes[5];
618   aNodes[5] = nd2;
619 }
620
621 //=======================================================================
622 //function : nbEdgeConnectivity
623 //purpose  : return number of the edges connected with the theNode.
624 //           if theEdges has connections with the other type of the
625 //           elements, return -1
626 //=======================================================================
627
628 static int nbEdgeConnectivity(const SMDS_MeshNode* theNode)
629 {
630   // SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator();
631   // int nb=0;
632   // while(elemIt->more()) {
633   //   elemIt->next();
634   //   nb++;
635   // }
636   // return nb;
637   return theNode->NbInverseElements();
638 }
639
640 //=======================================================================
641 //function : getNodesFromTwoTria
642 //purpose  : 
643 //=======================================================================
644
645 static bool getNodesFromTwoTria(const SMDS_MeshElement * theTria1,
646                                 const SMDS_MeshElement * theTria2,
647                                 vector< const SMDS_MeshNode*>& N1,
648                                 vector< const SMDS_MeshNode*>& N2)
649 {
650   N1.assign( theTria1->begin_nodes(), theTria1->end_nodes() );
651   if ( N1.size() < 6 ) return false;
652   N2.assign( theTria2->begin_nodes(), theTria2->end_nodes() );
653   if ( N2.size() < 6 ) return false;
654
655   int sames[3] = {-1,-1,-1};
656   int nbsames = 0;
657   int i, j;
658   for(i=0; i<3; i++) {
659     for(j=0; j<3; j++) {
660       if(N1[i]==N2[j]) {
661         sames[i] = j;
662         nbsames++;
663         break;
664       }
665     }
666   }
667   if(nbsames!=2) return false;
668   if(sames[0]>-1) {
669     shiftNodesQuadTria(N1);
670     if(sames[1]>-1) {
671       shiftNodesQuadTria(N1);
672     }
673   }
674   i = sames[0] + sames[1] + sames[2];
675   for(; i<2; i++) {
676     shiftNodesQuadTria(N2);
677   }
678   // now we receive following N1 and N2 (using numeration as in the image below)
679   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
680   // i.e. first nodes from both arrays form a new diagonal
681   return true;
682 }
683
684 //=======================================================================
685 //function : InverseDiag
686 //purpose  : Replace two neighbour triangles with ones built on the same 4 nodes
687 //           but having other common link.
688 //           Return False if args are improper
689 //=======================================================================
690
691 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1,
692                                     const SMDS_MeshElement * theTria2 )
693 {
694   myLastCreatedElems.Clear();
695   myLastCreatedNodes.Clear();
696
697   if (!theTria1 || !theTria2)
698     return false;
699
700   const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( theTria1 );
701   if (!F1) return false;
702   const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( theTria2 );
703   if (!F2) return false;
704   if ((theTria1->GetEntityType() == SMDSEntity_Triangle) &&
705       (theTria2->GetEntityType() == SMDSEntity_Triangle)) {
706
707     //  1 +--+ A  theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
708     //    | /|    theTria2: ( B A 2 ) B->1 ( 1 A 2 )   |\ |
709     //    |/ |                                         | \|
710     //  B +--+ 2                                     B +--+ 2
711
712     // put nodes in array and find out indices of the same ones
713     const SMDS_MeshNode* aNodes [6];
714     int sameInd [] = { -1, -1, -1, -1, -1, -1 };
715     int i = 0;
716     SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
717     while ( it->more() ) {
718       aNodes[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
719
720       if ( i > 2 ) // theTria2
721         // find same node of theTria1
722         for ( int j = 0; j < 3; j++ )
723           if ( aNodes[ i ] == aNodes[ j ]) {
724             sameInd[ j ] = i;
725             sameInd[ i ] = j;
726             break;
727           }
728       // next
729       i++;
730       if ( i == 3 ) {
731         if ( it->more() )
732           return false; // theTria1 is not a triangle
733         it = theTria2->nodesIterator();
734       }
735       if ( i == 6 && it->more() )
736         return false; // theTria2 is not a triangle
737     }
738
739     // find indices of 1,2 and of A,B in theTria1
740     int iA = -1, iB = 0, i1 = 0, i2 = 0;
741     for ( i = 0; i < 6; i++ ) {
742       if ( sameInd [ i ] == -1 ) {
743         if ( i < 3 ) i1 = i;
744         else         i2 = i;
745       }
746       else if (i < 3) {
747         if ( iA >= 0) iB = i;
748         else          iA = i;
749       }
750     }
751     // nodes 1 and 2 should not be the same
752     if ( aNodes[ i1 ] == aNodes[ i2 ] )
753       return false;
754
755     // theTria1: A->2
756     aNodes[ iA ] = aNodes[ i2 ];
757     // theTria2: B->1
758     aNodes[ sameInd[ iB ]] = aNodes[ i1 ];
759
760     GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 );
761     GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 );
762
763     return true;
764
765   } // end if(F1 && F2)
766
767   // check case of quadratic faces
768   if (theTria1->GetEntityType() != SMDSEntity_Quad_Triangle &&
769       theTria1->GetEntityType() != SMDSEntity_BiQuad_Triangle)
770     return false;
771   if (theTria2->GetEntityType() != SMDSEntity_Quad_Triangle&&
772       theTria2->GetEntityType() != SMDSEntity_BiQuad_Triangle)
773     return false;
774
775   //       5
776   //  1 +--+--+ 2  theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
777   //    |    /|    theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
778   //    |   / |
779   //  7 +  +  + 6
780   //    | /9  |
781   //    |/    |
782   //  4 +--+--+ 3
783   //       8
784
785   vector< const SMDS_MeshNode* > N1;
786   vector< const SMDS_MeshNode* > N2;
787   if(!getNodesFromTwoTria(theTria1,theTria2,N1,N2))
788     return false;
789   // now we receive following N1 and N2 (using numeration as above image)
790   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
791   // i.e. first nodes from both arrays determ new diagonal
792
793   vector< const SMDS_MeshNode*> N1new( N1.size() );
794   vector< const SMDS_MeshNode*> N2new( N2.size() );
795   N1new.back() = N1.back(); // central node of biquadratic
796   N2new.back() = N2.back();
797   N1new[0] = N1[0];  N2new[0] = N1[0];
798   N1new[1] = N2[0];  N2new[1] = N1[1];
799   N1new[2] = N2[1];  N2new[2] = N2[0];
800   N1new[3] = N1[4];  N2new[3] = N1[3];
801   N1new[4] = N2[3];  N2new[4] = N2[5];
802   N1new[5] = N1[5];  N2new[5] = N1[4];
803   // change nodes in faces
804   GetMeshDS()->ChangeElementNodes( theTria1, &N1new[0], N1new.size() );
805   GetMeshDS()->ChangeElementNodes( theTria2, &N2new[0], N2new.size() );
806
807   // move the central node of biquadratic triangle
808   SMESH_MesherHelper helper( *GetMesh() );
809   for ( int is2nd = 0; is2nd < 2; ++is2nd )
810   {
811     const SMDS_MeshElement*         tria = is2nd ? theTria2 : theTria1;
812     vector< const SMDS_MeshNode*>& nodes = is2nd ? N2new : N1new;
813     if ( nodes.size() < 7 )
814       continue;
815     helper.SetSubShape( tria->getshapeId() );
816     const TopoDS_Face& F = TopoDS::Face( helper.GetSubShape() );
817     gp_Pnt xyz;
818     if ( F.IsNull() )
819     {
820       xyz = ( SMESH_TNodeXYZ( nodes[3] ) +
821               SMESH_TNodeXYZ( nodes[4] ) +
822               SMESH_TNodeXYZ( nodes[5] )) / 3.;
823     }
824     else
825     {
826       bool checkUV;
827       gp_XY uv = ( helper.GetNodeUV( F, nodes[3], nodes[2], &checkUV ) +
828                    helper.GetNodeUV( F, nodes[4], nodes[0], &checkUV ) +
829                    helper.GetNodeUV( F, nodes[5], nodes[1], &checkUV )) / 3.;
830       TopLoc_Location loc;
831       Handle(Geom_Surface) S = BRep_Tool::Surface(F,loc);
832       xyz = S->Value( uv.X(), uv.Y() );
833       xyz.Transform( loc );
834       if ( nodes[6]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE &&  // set UV
835            nodes[6]->getshapeId() > 0 )
836         GetMeshDS()->SetNodeOnFace( nodes[6], nodes[6]->getshapeId(), uv.X(), uv.Y() );
837     }
838     GetMeshDS()->MoveNode( nodes[6], xyz.X(), xyz.Y(), xyz.Z() );
839   }
840   return true;
841 }
842
843 //=======================================================================
844 //function : findTriangles
845 //purpose  : find triangles sharing theNode1-theNode2 link
846 //=======================================================================
847
848 static bool findTriangles(const SMDS_MeshNode *    theNode1,
849                           const SMDS_MeshNode *    theNode2,
850                           const SMDS_MeshElement*& theTria1,
851                           const SMDS_MeshElement*& theTria2)
852 {
853   if ( !theNode1 || !theNode2 ) return false;
854
855   theTria1 = theTria2 = 0;
856
857   set< const SMDS_MeshElement* > emap;
858   SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face);
859   while (it->more()) {
860     const SMDS_MeshElement* elem = it->next();
861     if ( elem->NbCornerNodes() == 3 )
862       emap.insert( elem );
863   }
864   it = theNode2->GetInverseElementIterator(SMDSAbs_Face);
865   while (it->more()) {
866     const SMDS_MeshElement* elem = it->next();
867     if ( emap.count( elem )) {
868       if ( !theTria1 )
869       {
870         theTria1 = elem;
871       }
872       else  
873       {
874         theTria2 = elem;
875         // theTria1 must be element with minimum ID
876         if ( theTria2->GetID() < theTria1->GetID() )
877           std::swap( theTria2, theTria1 );
878         return true;
879       }
880     }
881   }
882   return false;
883 }
884
885 //=======================================================================
886 //function : InverseDiag
887 //purpose  : Replace two neighbour triangles sharing theNode1-theNode2 link
888 //           with ones built on the same 4 nodes but having other common link.
889 //           Return false if proper faces not found
890 //=======================================================================
891
892 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
893                                     const SMDS_MeshNode * theNode2)
894 {
895   myLastCreatedElems.Clear();
896   myLastCreatedNodes.Clear();
897
898   const SMDS_MeshElement *tr1, *tr2;
899   if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
900     return false;
901
902   const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
903   if (!F1) return false;
904   const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
905   if (!F2) return false;
906   if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
907       (tr2->GetEntityType() == SMDSEntity_Triangle)) {
908
909     //  1 +--+ A  tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
910     //    | /|    tr2: ( B A 2 ) B->1 ( 1 A 2 )   |\ |
911     //    |/ |                                    | \|
912     //  B +--+ 2                                B +--+ 2
913
914     // put nodes in array
915     // and find indices of 1,2 and of A in tr1 and of B in tr2
916     int i, iA1 = 0, i1 = 0;
917     const SMDS_MeshNode* aNodes1 [3];
918     SMDS_ElemIteratorPtr it;
919     for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) {
920       aNodes1[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
921       if ( aNodes1[ i ] == theNode1 )
922         iA1 = i; // node A in tr1
923       else if ( aNodes1[ i ] != theNode2 )
924         i1 = i;  // node 1
925     }
926     int iB2 = 0, i2 = 0;
927     const SMDS_MeshNode* aNodes2 [3];
928     for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) {
929       aNodes2[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
930       if ( aNodes2[ i ] == theNode2 )
931         iB2 = i; // node B in tr2
932       else if ( aNodes2[ i ] != theNode1 )
933         i2 = i;  // node 2
934     }
935
936     // nodes 1 and 2 should not be the same
937     if ( aNodes1[ i1 ] == aNodes2[ i2 ] )
938       return false;
939
940     // tr1: A->2
941     aNodes1[ iA1 ] = aNodes2[ i2 ];
942     // tr2: B->1
943     aNodes2[ iB2 ] = aNodes1[ i1 ];
944
945     GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 );
946     GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 );
947
948     return true;
949   }
950
951   // check case of quadratic faces
952   return InverseDiag(tr1,tr2);
953 }
954
955 //=======================================================================
956 //function : getQuadrangleNodes
957 //purpose  : fill theQuadNodes - nodes of a quadrangle resulting from
958 //           fusion of triangles tr1 and tr2 having shared link on
959 //           theNode1 and theNode2
960 //=======================================================================
961
962 bool getQuadrangleNodes(const SMDS_MeshNode *    theQuadNodes [],
963                         const SMDS_MeshNode *    theNode1,
964                         const SMDS_MeshNode *    theNode2,
965                         const SMDS_MeshElement * tr1,
966                         const SMDS_MeshElement * tr2 )
967 {
968   if( tr1->NbNodes() != tr2->NbNodes() )
969     return false;
970   // find the 4-th node to insert into tr1
971   const SMDS_MeshNode* n4 = 0;
972   SMDS_ElemIteratorPtr it = tr2->nodesIterator();
973   int i=0;
974   while ( !n4 && i<3 ) {
975     const SMDS_MeshNode * n = cast2Node( it->next() );
976     i++;
977     bool isDiag = ( n == theNode1 || n == theNode2 );
978     if ( !isDiag )
979       n4 = n;
980   }
981   // Make an array of nodes to be in a quadrangle
982   int iNode = 0, iFirstDiag = -1;
983   it = tr1->nodesIterator();
984   i=0;
985   while ( i<3 ) {
986     const SMDS_MeshNode * n = cast2Node( it->next() );
987     i++;
988     bool isDiag = ( n == theNode1 || n == theNode2 );
989     if ( isDiag ) {
990       if ( iFirstDiag < 0 )
991         iFirstDiag = iNode;
992       else if ( iNode - iFirstDiag == 1 )
993         theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes
994     }
995     else if ( n == n4 ) {
996       return false; // tr1 and tr2 should not have all the same nodes
997     }
998     theQuadNodes[ iNode++ ] = n;
999   }
1000   if ( iNode == 3 ) // diagonal nodes have 0 and 2 indices
1001     theQuadNodes[ iNode ] = n4;
1002
1003   return true;
1004 }
1005
1006 //=======================================================================
1007 //function : DeleteDiag
1008 //purpose  : Replace two neighbour triangles sharing theNode1-theNode2 link
1009 //           with a quadrangle built on the same 4 nodes.
1010 //           Return false if proper faces not found
1011 //=======================================================================
1012
1013 bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
1014                                    const SMDS_MeshNode * theNode2)
1015 {
1016   myLastCreatedElems.Clear();
1017   myLastCreatedNodes.Clear();
1018
1019   const SMDS_MeshElement *tr1, *tr2;
1020   if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
1021     return false;
1022
1023   const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
1024   if (!F1) return false;
1025   const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
1026   if (!F2) return false;
1027   SMESHDS_Mesh * aMesh = GetMeshDS();
1028
1029   if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
1030       (tr2->GetEntityType() == SMDSEntity_Triangle)) {
1031
1032     const SMDS_MeshNode* aNodes [ 4 ];
1033     if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 ))
1034       return false;
1035
1036     const SMDS_MeshElement* newElem = 0;
1037     newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3] );
1038     myLastCreatedElems.Append(newElem);
1039     AddToSameGroups( newElem, tr1, aMesh );
1040     int aShapeId = tr1->getshapeId();
1041     if ( aShapeId )
1042       {
1043         aMesh->SetMeshElementOnShape( newElem, aShapeId );
1044       }
1045     aMesh->RemoveElement( tr1 );
1046     aMesh->RemoveElement( tr2 );
1047
1048     return true;
1049   }
1050
1051   // check case of quadratic faces
1052   if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
1053     return false;
1054   if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
1055     return false;
1056
1057   //       5
1058   //  1 +--+--+ 2  tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
1059   //    |    /|    tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
1060   //    |   / |
1061   //  7 +  +  + 6
1062   //    | /9  |
1063   //    |/    |
1064   //  4 +--+--+ 3
1065   //       8
1066
1067   vector< const SMDS_MeshNode* > N1;
1068   vector< const SMDS_MeshNode* > N2;
1069   if(!getNodesFromTwoTria(tr1,tr2,N1,N2))
1070     return false;
1071   // now we receive following N1 and N2 (using numeration as above image)
1072   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
1073   // i.e. first nodes from both arrays determ new diagonal
1074
1075   const SMDS_MeshNode* aNodes[8];
1076   aNodes[0] = N1[0];
1077   aNodes[1] = N1[1];
1078   aNodes[2] = N2[0];
1079   aNodes[3] = N2[1];
1080   aNodes[4] = N1[3];
1081   aNodes[5] = N2[5];
1082   aNodes[6] = N2[3];
1083   aNodes[7] = N1[5];
1084
1085   const SMDS_MeshElement* newElem = 0;
1086   newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
1087                             aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
1088   myLastCreatedElems.Append(newElem);
1089   AddToSameGroups( newElem, tr1, aMesh );
1090   int aShapeId = tr1->getshapeId();
1091   if ( aShapeId )
1092     {
1093       aMesh->SetMeshElementOnShape( newElem, aShapeId );
1094     }
1095   aMesh->RemoveElement( tr1 );
1096   aMesh->RemoveElement( tr2 );
1097
1098   // remove middle node (9)
1099   GetMeshDS()->RemoveNode( N1[4] );
1100
1101   return true;
1102 }
1103
1104 //=======================================================================
1105 //function : Reorient
1106 //purpose  : Reverse theElement orientation
1107 //=======================================================================
1108
1109 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
1110 {
1111   myLastCreatedElems.Clear();
1112   myLastCreatedNodes.Clear();
1113
1114   if (!theElem)
1115     return false;
1116   SMDS_ElemIteratorPtr it = theElem->nodesIterator();
1117   if ( !it || !it->more() )
1118     return false;
1119
1120   const SMDSAbs_ElementType type = theElem->GetType();
1121   if ( type < SMDSAbs_Edge || type > SMDSAbs_Volume )
1122     return false;
1123
1124   const SMDSAbs_EntityType geomType = theElem->GetEntityType();
1125   if ( geomType == SMDSEntity_Polyhedra ) // polyhedron
1126   {
1127     const SMDS_VtkVolume* aPolyedre =
1128       dynamic_cast<const SMDS_VtkVolume*>( theElem );
1129     if (!aPolyedre) {
1130       MESSAGE("Warning: bad volumic element");
1131       return false;
1132     }
1133     const int nbFaces = aPolyedre->NbFaces();
1134     vector<const SMDS_MeshNode *> poly_nodes;
1135     vector<int> quantities (nbFaces);
1136
1137     // reverse each face of the polyedre
1138     for (int iface = 1; iface <= nbFaces; iface++) {
1139       int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
1140       quantities[iface - 1] = nbFaceNodes;
1141
1142       for (inode = nbFaceNodes; inode >= 1; inode--) {
1143         const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
1144         poly_nodes.push_back(curNode);
1145       }
1146     }
1147     return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
1148   }
1149   else // other elements
1150   {
1151     vector<const SMDS_MeshNode*> nodes( theElem->begin_nodes(), theElem->end_nodes() );
1152     const std::vector<int>& interlace = SMDS_MeshCell::reverseSmdsOrder( geomType, nodes.size() );
1153     if ( interlace.empty() )
1154     {
1155       std::reverse( nodes.begin(), nodes.end() ); // obsolete, just in case
1156     }
1157     else
1158     {
1159       SMDS_MeshCell::applyInterlace( interlace, nodes );
1160     }
1161     return GetMeshDS()->ChangeElementNodes( theElem, &nodes[0], nodes.size() );
1162   }
1163   return false;
1164 }
1165
1166 //================================================================================
1167 /*!
1168  * \brief Reorient faces.
1169  * \param theFaces - the faces to reorient. If empty the whole mesh is meant
1170  * \param theDirection - desired direction of normal of \a theFace
1171  * \param theFace - one of \a theFaces that should be oriented according to
1172  *        \a theDirection and whose orientation defines orientation of other faces
1173  * \return number of reoriented faces.
1174  */
1175 //================================================================================
1176
1177 int SMESH_MeshEditor::Reorient2D (TIDSortedElemSet &       theFaces,
1178                                   const gp_Dir&            theDirection,
1179                                   const SMDS_MeshElement * theFace)
1180 {
1181   int nbReori = 0;
1182   if ( !theFace || theFace->GetType() != SMDSAbs_Face ) return nbReori;
1183
1184   if ( theFaces.empty() )
1185   {
1186     SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator(/*idInceasingOrder=*/true);
1187     while ( fIt->more() )
1188       theFaces.insert( theFaces.end(), fIt->next() );
1189   }
1190
1191   // orient theFace according to theDirection
1192   gp_XYZ normal;
1193   SMESH_MeshAlgos::FaceNormal( theFace, normal, /*normalized=*/false );
1194   if ( normal * theDirection.XYZ() < 0 )
1195     nbReori += Reorient( theFace );
1196
1197   // Orient other faces
1198
1199   set< const SMDS_MeshElement* > startFaces, visitedFaces;
1200   TIDSortedElemSet avoidSet;
1201   set< SMESH_TLink > checkedLinks;
1202   pair< set< SMESH_TLink >::iterator, bool > linkIt_isNew;
1203
1204   if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces
1205     theFaces.erase( theFace );
1206   startFaces.insert( theFace );
1207
1208   int nodeInd1, nodeInd2;
1209   const SMDS_MeshElement*           otherFace;
1210   vector< const SMDS_MeshElement* > facesNearLink;
1211   vector< std::pair< int, int > >   nodeIndsOfFace;
1212
1213   set< const SMDS_MeshElement* >::iterator startFace = startFaces.begin();
1214   while ( !startFaces.empty() )
1215   {
1216     startFace = startFaces.begin();
1217     theFace = *startFace;
1218     startFaces.erase( startFace );
1219     if ( !visitedFaces.insert( theFace ).second )
1220       continue;
1221
1222     avoidSet.clear();
1223     avoidSet.insert(theFace);
1224
1225     NLink link( theFace->GetNode( 0 ), (SMDS_MeshNode *) 0 );
1226
1227     const int nbNodes = theFace->NbCornerNodes();
1228     for ( int i = 0; i < nbNodes; ++i ) // loop on links of theFace
1229     {
1230       link.second = theFace->GetNode(( i+1 ) % nbNodes );
1231       linkIt_isNew = checkedLinks.insert( link );
1232       if ( !linkIt_isNew.second )
1233       {
1234         // link has already been checked and won't be encountered more
1235         // if the group (theFaces) is manifold
1236         //checkedLinks.erase( linkIt_isNew.first );
1237       }
1238       else
1239       {
1240         facesNearLink.clear();
1241         nodeIndsOfFace.clear();
1242         while (( otherFace = SMESH_MeshAlgos::FindFaceInSet( link.first, link.second,
1243                                                              theFaces, avoidSet,
1244                                                              &nodeInd1, &nodeInd2 )))
1245           if ( otherFace != theFace)
1246           {
1247             facesNearLink.push_back( otherFace );
1248             nodeIndsOfFace.push_back( make_pair( nodeInd1, nodeInd2 ));
1249             avoidSet.insert( otherFace );
1250           }
1251         if ( facesNearLink.size() > 1 )
1252         {
1253           // NON-MANIFOLD mesh shell !
1254           // select a face most co-directed with theFace,
1255           // other faces won't be visited this time
1256           gp_XYZ NF, NOF;
1257           SMESH_MeshAlgos::FaceNormal( theFace, NF, /*normalized=*/false );
1258           double proj, maxProj = -1;
1259           for ( size_t i = 0; i < facesNearLink.size(); ++i ) {
1260             SMESH_MeshAlgos::FaceNormal( facesNearLink[i], NOF, /*normalized=*/false );
1261             if (( proj = Abs( NF * NOF )) > maxProj ) {
1262               maxProj = proj;
1263               otherFace = facesNearLink[i];
1264               nodeInd1  = nodeIndsOfFace[i].first;
1265               nodeInd2  = nodeIndsOfFace[i].second;
1266             }
1267           }
1268           // not to visit rejected faces
1269           for ( size_t i = 0; i < facesNearLink.size(); ++i )
1270             if ( facesNearLink[i] != otherFace && theFaces.size() > 1 )
1271               visitedFaces.insert( facesNearLink[i] );
1272         }
1273         else if ( facesNearLink.size() == 1 )
1274         {
1275           otherFace = facesNearLink[0];
1276           nodeInd1  = nodeIndsOfFace.back().first;
1277           nodeInd2  = nodeIndsOfFace.back().second;
1278         }
1279         if ( otherFace && otherFace != theFace)
1280         {
1281           // link must be reverse in otherFace if orientation ot otherFace
1282           // is same as that of theFace
1283           if ( abs(nodeInd2-nodeInd1) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1284           {
1285             nbReori += Reorient( otherFace );
1286           }
1287           startFaces.insert( otherFace );
1288         }
1289       }
1290       std::swap( link.first, link.second ); // reverse the link
1291     }
1292   }
1293   return nbReori;
1294 }
1295
1296 //================================================================================
1297 /*!
1298  * \brief Reorient faces basing on orientation of adjacent volumes.
1299  * \param theFaces - faces to reorient. If empty, all mesh faces are treated.
1300  * \param theVolumes - reference volumes.
1301  * \param theOutsideNormal - to orient faces to have their normal
1302  *        pointing either \a outside or \a inside the adjacent volumes.
1303  * \return number of reoriented faces.
1304  */
1305 //================================================================================
1306
1307 int SMESH_MeshEditor::Reorient2DBy3D (TIDSortedElemSet & theFaces,
1308                                       TIDSortedElemSet & theVolumes,
1309                                       const bool         theOutsideNormal)
1310 {
1311   int nbReori = 0;
1312
1313   SMDS_ElemIteratorPtr faceIt;
1314   if ( theFaces.empty() )
1315     faceIt = GetMeshDS()->elementsIterator( SMDSAbs_Face );
1316   else
1317     faceIt = elemSetIterator( theFaces );
1318
1319   vector< const SMDS_MeshNode* > faceNodes;
1320   TIDSortedElemSet checkedVolumes;
1321   set< const SMDS_MeshNode* > faceNodesSet;
1322   SMDS_VolumeTool volumeTool;
1323
1324   while ( faceIt->more() ) // loop on given faces
1325   {
1326     const SMDS_MeshElement* face = faceIt->next();
1327     if ( face->GetType() != SMDSAbs_Face )
1328       continue;
1329
1330     const size_t nbCornersNodes = face->NbCornerNodes();
1331     faceNodes.assign( face->begin_nodes(), face->end_nodes() );
1332
1333     checkedVolumes.clear();
1334     SMDS_ElemIteratorPtr vIt = faceNodes[ 0 ]->GetInverseElementIterator( SMDSAbs_Volume );
1335     while ( vIt->more() )
1336     {
1337       const SMDS_MeshElement* volume = vIt->next();
1338
1339       if ( !checkedVolumes.insert( volume ).second )
1340         continue;
1341       if ( !theVolumes.empty() && !theVolumes.count( volume ))
1342         continue;
1343
1344       // is volume adjacent?
1345       bool allNodesCommon = true;
1346       for ( size_t iN = 1; iN < nbCornersNodes && allNodesCommon; ++iN )
1347         allNodesCommon = ( volume->GetNodeIndex( faceNodes[ iN ]) > -1 );
1348       if ( !allNodesCommon )
1349         continue;
1350
1351       // get nodes of a corresponding volume facet
1352       faceNodesSet.clear();
1353       faceNodesSet.insert( faceNodes.begin(), faceNodes.end() );
1354       volumeTool.Set( volume );
1355       int facetID = volumeTool.GetFaceIndex( faceNodesSet );
1356       if ( facetID < 0 ) continue;
1357       volumeTool.SetExternalNormal();
1358       const SMDS_MeshNode** facetNodes = volumeTool.GetFaceNodes( facetID );
1359
1360       // compare order of faceNodes and facetNodes
1361       const int iQ = 1 + ( nbCornersNodes < faceNodes.size() );
1362       int iNN[2];
1363       for ( int i = 0; i < 2; ++i )
1364       {
1365         const SMDS_MeshNode* n = facetNodes[ i*iQ ];
1366         for ( size_t iN = 0; iN < nbCornersNodes; ++iN )
1367           if ( faceNodes[ iN ] == n )
1368           {
1369             iNN[ i ] = iN;
1370             break;
1371           }
1372       }
1373       bool isOutside = Abs( iNN[0]-iNN[1] ) == 1 ? iNN[0] < iNN[1] : iNN[0] > iNN[1];
1374       if ( isOutside != theOutsideNormal )
1375         nbReori += Reorient( face );
1376     }
1377   }  // loop on given faces
1378
1379   return nbReori;
1380 }
1381
1382 //=======================================================================
1383 //function : getBadRate
1384 //purpose  :
1385 //=======================================================================
1386
1387 static double getBadRate (const SMDS_MeshElement*               theElem,
1388                           SMESH::Controls::NumericalFunctorPtr& theCrit)
1389 {
1390   SMESH::Controls::TSequenceOfXYZ P;
1391   if ( !theElem || !theCrit->GetPoints( theElem, P ))
1392     return 1e100;
1393   return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1394   //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1395 }
1396
1397 //=======================================================================
1398 //function : QuadToTri
1399 //purpose  : Cut quadrangles into triangles.
1400 //           theCrit is used to select a diagonal to cut
1401 //=======================================================================
1402
1403 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet &                   theElems,
1404                                   SMESH::Controls::NumericalFunctorPtr theCrit)
1405 {
1406   myLastCreatedElems.Clear();
1407   myLastCreatedNodes.Clear();
1408
1409   if ( !theCrit.get() )
1410     return false;
1411
1412   SMESHDS_Mesh * aMesh = GetMeshDS();
1413
1414   Handle(Geom_Surface) surface;
1415   SMESH_MesherHelper   helper( *GetMesh() );
1416
1417   TIDSortedElemSet::iterator itElem;
1418   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
1419   {
1420     const SMDS_MeshElement* elem = *itElem;
1421     if ( !elem || elem->GetType() != SMDSAbs_Face )
1422       continue;
1423     if ( elem->NbCornerNodes() != 4 )
1424       continue;
1425
1426     // retrieve element nodes
1427     vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1428
1429     // compare two sets of possible triangles
1430     double aBadRate1, aBadRate2; // to what extent a set is bad
1431     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1432     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1433     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1434
1435     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1436     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1437     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1438
1439     const int aShapeId = FindShape( elem );
1440     const SMDS_MeshElement* newElem1 = 0;
1441     const SMDS_MeshElement* newElem2 = 0;
1442
1443     if ( !elem->IsQuadratic() ) // split liner quadrangle
1444     {
1445       // for MaxElementLength2D functor we return minimum diagonal for splitting,
1446       // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1447       if ( aBadRate1 <= aBadRate2 ) {
1448         // tr1 + tr2 is better
1449         newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1450         newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1451       }
1452       else {
1453         // tr3 + tr4 is better
1454         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1455         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1456       }
1457     }
1458     else // split quadratic quadrangle
1459     {
1460       helper.SetIsQuadratic( true );
1461       helper.SetIsBiQuadratic( aNodes.size() == 9 );
1462
1463       helper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem ));
1464       if ( aNodes.size() == 9 )
1465       {
1466         helper.SetIsBiQuadratic( true );
1467         if ( aBadRate1 <= aBadRate2 )
1468           helper.AddTLinkNode( aNodes[0], aNodes[2], aNodes[8] );
1469         else
1470           helper.AddTLinkNode( aNodes[1], aNodes[3], aNodes[8] );
1471       }
1472       // create a new element
1473       if ( aBadRate1 <= aBadRate2 ) {
1474         newElem1 = helper.AddFace( aNodes[2], aNodes[3], aNodes[0] );
1475         newElem2 = helper.AddFace( aNodes[2], aNodes[0], aNodes[1] );
1476       }
1477       else {
1478         newElem1 = helper.AddFace( aNodes[3], aNodes[0], aNodes[1] );
1479         newElem2 = helper.AddFace( aNodes[3], aNodes[1], aNodes[2] );
1480       }
1481     } // quadratic case
1482
1483     // care of a new element
1484
1485     myLastCreatedElems.Append(newElem1);
1486     myLastCreatedElems.Append(newElem2);
1487     AddToSameGroups( newElem1, elem, aMesh );
1488     AddToSameGroups( newElem2, elem, aMesh );
1489
1490     // put a new triangle on the same shape
1491     if ( aShapeId )
1492       aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1493     aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1494
1495     aMesh->RemoveElement( elem );
1496   }
1497   return true;
1498 }
1499
1500 //=======================================================================
1501 /*!
1502  * \brief Split each of given quadrangles into 4 triangles.
1503  * \param theElems - The faces to be splitted. If empty all faces are split.
1504  */
1505 //=======================================================================
1506
1507 void SMESH_MeshEditor::QuadTo4Tri (TIDSortedElemSet & theElems)
1508 {
1509   myLastCreatedElems.Clear();
1510   myLastCreatedNodes.Clear();
1511
1512   SMESH_MesherHelper helper( *GetMesh() );
1513   helper.SetElementsOnShape( true );
1514
1515   SMDS_ElemIteratorPtr faceIt;
1516   if ( theElems.empty() ) faceIt = GetMeshDS()->elementsIterator(SMDSAbs_Face);
1517   else                    faceIt = elemSetIterator( theElems );
1518
1519   bool   checkUV;
1520   gp_XY  uv [9]; uv[8] = gp_XY(0,0);
1521   gp_XYZ xyz[9];
1522   vector< const SMDS_MeshNode* > nodes;
1523   SMESHDS_SubMesh*               subMeshDS = 0;
1524   TopoDS_Face                    F;
1525   Handle(Geom_Surface)           surface;
1526   TopLoc_Location                loc;
1527
1528   while ( faceIt->more() )
1529   {
1530     const SMDS_MeshElement* quad = faceIt->next();
1531     if ( !quad || quad->NbCornerNodes() != 4 )
1532       continue;
1533
1534     // get a surface the quad is on
1535
1536     if ( quad->getshapeId() < 1 )
1537     {
1538       F.Nullify();
1539       helper.SetSubShape( 0 );
1540       subMeshDS = 0;
1541     }
1542     else if ( quad->getshapeId() != helper.GetSubShapeID() )
1543     {
1544       helper.SetSubShape( quad->getshapeId() );
1545       if ( !helper.GetSubShape().IsNull() &&
1546            helper.GetSubShape().ShapeType() == TopAbs_FACE )
1547       {
1548         F = TopoDS::Face( helper.GetSubShape() );
1549         surface = BRep_Tool::Surface( F, loc );
1550         subMeshDS = GetMeshDS()->MeshElements( quad->getshapeId() );
1551       }
1552       else
1553       {
1554         helper.SetSubShape( 0 );
1555         subMeshDS = 0;
1556       }
1557     }
1558
1559     // create a central node
1560
1561     const SMDS_MeshNode* nCentral;
1562     nodes.assign( quad->begin_nodes(), quad->end_nodes() );
1563
1564     if ( nodes.size() == 9 )
1565     {
1566       nCentral = nodes.back();
1567     }
1568     else
1569     {
1570       size_t iN = 0;
1571       if ( F.IsNull() )
1572       {
1573         for ( ; iN < nodes.size(); ++iN )
1574           xyz[ iN ] = SMESH_TNodeXYZ( nodes[ iN ] );
1575
1576         for ( ; iN < 8; ++iN ) // mid-side points of a linear qudrangle
1577           xyz[ iN ] = 0.5 * ( xyz[ iN - 4 ] + xyz[( iN - 3 )%4 ] );
1578
1579         xyz[ 8 ] = helper.calcTFI( 0.5, 0.5,
1580                                    xyz[0], xyz[1], xyz[2], xyz[3],
1581                                    xyz[4], xyz[5], xyz[6], xyz[7] );
1582       }
1583       else
1584       {
1585         for ( ; iN < nodes.size(); ++iN )
1586           uv[ iN ] = helper.GetNodeUV( F, nodes[iN], nodes[(iN+2)%4], &checkUV );
1587
1588         for ( ; iN < 8; ++iN ) // UV of mid-side points of a linear qudrangle
1589           uv[ iN ] = helper.GetMiddleUV( surface, uv[ iN - 4 ], uv[( iN - 3 )%4 ] );
1590
1591         uv[ 8 ] = helper.calcTFI( 0.5, 0.5,
1592                                   uv[0], uv[1], uv[2], uv[3],
1593                                   uv[4], uv[5], uv[6], uv[7] );
1594
1595         gp_Pnt p = surface->Value( uv[8].X(), uv[8].Y() ).Transformed( loc );
1596         xyz[ 8 ] = p.XYZ();
1597       }
1598
1599       nCentral = helper.AddNode( xyz[8].X(), xyz[8].Y(), xyz[8].Z(), /*id=*/0,
1600                                  uv[8].X(), uv[8].Y() );
1601       myLastCreatedNodes.Append( nCentral );
1602     }
1603
1604     // create 4 triangles
1605
1606     helper.SetIsQuadratic  ( nodes.size() > 4 );
1607     helper.SetIsBiQuadratic( nodes.size() == 9 );
1608     if ( helper.GetIsQuadratic() )
1609       helper.AddTLinks( static_cast< const SMDS_MeshFace*>( quad ));
1610
1611     GetMeshDS()->RemoveFreeElement( quad, subMeshDS, /*fromGroups=*/false );
1612
1613     for ( int i = 0; i < 4; ++i )
1614     {
1615       SMDS_MeshElement* tria = helper.AddFace( nodes[ i ],
1616                                                nodes[(i+1)%4],
1617                                                nCentral );
1618       ReplaceElemInGroups( tria, quad, GetMeshDS() );
1619       myLastCreatedElems.Append( tria );
1620     }
1621   }
1622 }
1623
1624 //=======================================================================
1625 //function : BestSplit
1626 //purpose  : Find better diagonal for cutting.
1627 //=======================================================================
1628
1629 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement*              theQuad,
1630                                  SMESH::Controls::NumericalFunctorPtr theCrit)
1631 {
1632   myLastCreatedElems.Clear();
1633   myLastCreatedNodes.Clear();
1634
1635   if (!theCrit.get())
1636     return -1;
1637
1638   if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1639     return -1;
1640
1641   if( theQuad->NbNodes()==4 ||
1642       (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1643
1644     // retrieve element nodes
1645     const SMDS_MeshNode* aNodes [4];
1646     SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1647     int i = 0;
1648     //while (itN->more())
1649     while (i<4) {
1650       aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1651     }
1652     // compare two sets of possible triangles
1653     double aBadRate1, aBadRate2; // to what extent a set is bad
1654     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1655     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1656     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1657
1658     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1659     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1660     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1661     // for MaxElementLength2D functor we return minimum diagonal for splitting,
1662     // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1663     if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1664       return 1; // diagonal 1-3
1665
1666     return 2; // diagonal 2-4
1667   }
1668   return -1;
1669 }
1670
1671 namespace
1672 {
1673   // Methods of splitting volumes into tetra
1674
1675   const int theHexTo5_1[5*4+1] =
1676     {
1677       0, 1, 2, 5,    0, 4, 5, 7,     0, 2, 3, 7,    2, 5, 6, 7,     0, 5, 2, 7,   -1
1678     };
1679   const int theHexTo5_2[5*4+1] =
1680     {
1681       1, 2, 3, 6,    1, 4, 5, 6,     0, 1, 3, 4,    3, 4, 6, 7,     1, 3, 4, 6,   -1
1682     };
1683   const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1684
1685   const int theHexTo6_1[6*4+1] =
1686     {
1687       1, 5, 6, 0,    0, 1, 2, 6,     0, 4, 5, 6,    0, 4, 6, 7,     0, 2, 3, 6,   0, 3, 7, 6,  -1
1688     };
1689   const int theHexTo6_2[6*4+1] =
1690     {
1691       2, 6, 7, 1,    1, 2, 3, 7,     1, 5, 6, 7,    1, 5, 7, 4,     1, 3, 0, 7,   1, 0, 4, 7,  -1
1692     };
1693   const int theHexTo6_3[6*4+1] =
1694     {
1695       3, 7, 4, 2,    2, 3, 0, 4,     2, 6, 7, 4,    2, 6, 4, 5,     2, 0, 1, 4,   2, 1, 5, 4,  -1
1696     };
1697   const int theHexTo6_4[6*4+1] =
1698     {
1699       0, 4, 5, 3,    3, 0, 1, 5,     3, 7, 4, 5,    3, 7, 5, 6,     3, 1, 2, 5,   3, 2, 6, 5,  -1
1700     };
1701   const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1702
1703   const int thePyraTo2_1[2*4+1] =
1704     {
1705       0, 1, 2, 4,    0, 2, 3, 4,   -1
1706     };
1707   const int thePyraTo2_2[2*4+1] =
1708     {
1709       1, 2, 3, 4,    1, 3, 0, 4,   -1
1710     };
1711   const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1712
1713   const int thePentaTo3_1[3*4+1] =
1714     {
1715       0, 1, 2, 3,    1, 3, 4, 2,     2, 3, 4, 5,    -1
1716     };
1717   const int thePentaTo3_2[3*4+1] =
1718     {
1719       1, 2, 0, 4,    2, 4, 5, 0,     0, 4, 5, 3,    -1
1720     };
1721   const int thePentaTo3_3[3*4+1] =
1722     {
1723       2, 0, 1, 5,    0, 5, 3, 1,     1, 5, 3, 4,    -1
1724     };
1725   const int thePentaTo3_4[3*4+1] =
1726     {
1727       0, 1, 2, 3,    1, 3, 4, 5,     2, 3, 1, 5,    -1
1728     };
1729   const int thePentaTo3_5[3*4+1] =
1730     {
1731       1, 2, 0, 4,    2, 4, 5, 3,     0, 4, 2, 3,    -1
1732     };
1733   const int thePentaTo3_6[3*4+1] =
1734     {
1735       2, 0, 1, 5,    0, 5, 3, 4,     1, 5, 0, 4,    -1
1736     };
1737   const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1738                                 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1739
1740   // Methods of splitting hexahedron into prisms
1741
1742   const int theHexTo4Prisms_BT[6*4+1] = // bottom-top
1743     {
1744       0, 1, 8, 4, 5, 9,    1, 2, 8, 5, 6, 9,    2, 3, 8, 6, 7, 9,   3, 0, 8, 7, 4, 9,    -1
1745     };
1746   const int theHexTo4Prisms_LR[6*4+1] = // left-right
1747     {
1748       1, 0, 8, 2, 3, 9,    0, 4, 8, 3, 7, 9,    4, 5, 8, 7, 6, 9,   5, 1, 8, 6, 2, 9,    -1
1749     };
1750   const int theHexTo4Prisms_FB[6*4+1] = // front-back
1751     {
1752       0, 3, 9, 1, 2, 8,    3, 7, 9, 2, 6, 8,    7, 4, 9, 6, 5, 8,   4, 0, 9, 5, 1, 8,    -1
1753     };
1754
1755   const int theHexTo2Prisms_BT_1[6*2+1] =
1756     {
1757       0, 1, 3, 4, 5, 7,    1, 2, 3, 5, 6, 7,   -1
1758     };
1759   const int theHexTo2Prisms_BT_2[6*2+1] =
1760     {
1761       0, 1, 2, 4, 5, 6,    0, 2, 3, 4, 6, 7,   -1
1762     };
1763   const int* theHexTo2Prisms_BT[2] = { theHexTo2Prisms_BT_1, theHexTo2Prisms_BT_2 };
1764
1765   const int theHexTo2Prisms_LR_1[6*2+1] =
1766     {
1767       1, 0, 4, 2, 3, 7,    1, 4, 5, 2, 7, 6,   -1
1768     };
1769   const int theHexTo2Prisms_LR_2[6*2+1] =
1770     {
1771       1, 0, 4, 2, 3, 7,    1, 4, 5, 2, 7, 6,   -1
1772     };
1773   const int* theHexTo2Prisms_LR[2] = { theHexTo2Prisms_LR_1, theHexTo2Prisms_LR_2 };
1774
1775   const int theHexTo2Prisms_FB_1[6*2+1] =
1776     {
1777       0, 3, 4, 1, 2, 5,    3, 7, 4, 2, 6, 5,   -1
1778     };
1779   const int theHexTo2Prisms_FB_2[6*2+1] =
1780     {
1781       0, 3, 7, 1, 2, 7,    0, 7, 4, 1, 6, 5,   -1
1782     };
1783   const int* theHexTo2Prisms_FB[2] = { theHexTo2Prisms_FB_1, theHexTo2Prisms_FB_2 };
1784
1785
1786   struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1787   {
1788     int _n1, _n2, _n3;
1789     TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1790     bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1791     bool hasAdjacentVol( const SMDS_MeshElement*    elem,
1792                          const SMDSAbs_GeometryType geom = SMDSGeom_TETRA) const;
1793   };
1794   struct TSplitMethod
1795   {
1796     int        _nbSplits;
1797     int        _nbCorners;
1798     const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1799     bool       _baryNode;     //!< additional node is to be created at cell barycenter
1800     bool       _ownConn;      //!< to delete _connectivity in destructor
1801     map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1802
1803     TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1804       : _nbSplits(nbTet), _nbCorners(4), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1805     ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1806     bool hasFacet( const TTriangleFacet& facet ) const
1807     {
1808       if ( _nbCorners == 4 )
1809       {
1810         const int* tetConn = _connectivity;
1811         for ( ; tetConn[0] >= 0; tetConn += 4 )
1812           if (( facet.contains( tetConn[0] ) +
1813                 facet.contains( tetConn[1] ) +
1814                 facet.contains( tetConn[2] ) +
1815                 facet.contains( tetConn[3] )) == 3 )
1816             return true;
1817       }
1818       else // prism, _nbCorners == 6
1819       {
1820         const int* prismConn = _connectivity;
1821         for ( ; prismConn[0] >= 0; prismConn += 6 )
1822         {
1823           if (( facet.contains( prismConn[0] ) &&
1824                 facet.contains( prismConn[1] ) &&
1825                 facet.contains( prismConn[2] ))
1826               ||
1827               ( facet.contains( prismConn[3] ) &&
1828                 facet.contains( prismConn[4] ) &&
1829                 facet.contains( prismConn[5] )))
1830             return true;
1831         }
1832       }
1833       return false;
1834     }
1835   };
1836
1837   //=======================================================================
1838   /*!
1839    * \brief return TSplitMethod for the given element to split into tetrahedra
1840    */
1841   //=======================================================================
1842
1843   TSplitMethod getTetraSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1844   {
1845     const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1846
1847     // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1848     // an edge and a face barycenter; tertaherdons are based on triangles and
1849     // a volume barycenter
1850     const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1851
1852     // Find out how adjacent volumes are split
1853
1854     vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1855     int hasAdjacentSplits = 0, maxTetConnSize = 0;
1856     for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1857     {
1858       int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1859       maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1860       if ( nbNodes < 4 ) continue;
1861
1862       list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1863       const int* nInd = vol.GetFaceNodesIndices( iF );
1864       if ( nbNodes == 4 )
1865       {
1866         TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1867         TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1868         if      ( t012.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t012 );
1869         else if ( t123.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t123 );
1870       }
1871       else
1872       {
1873         int iCom = 0; // common node of triangle faces to split into
1874         for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1875         {
1876           TTriangleFacet t012( nInd[ iQ * ( iCom             )],
1877                                nInd[ iQ * ( (iCom+1)%nbNodes )],
1878                                nInd[ iQ * ( (iCom+2)%nbNodes )]);
1879           TTriangleFacet t023( nInd[ iQ * ( iCom             )],
1880                                nInd[ iQ * ( (iCom+2)%nbNodes )],
1881                                nInd[ iQ * ( (iCom+3)%nbNodes )]);
1882           if ( t012.hasAdjacentVol( vol.Element() ) && t023.hasAdjacentVol( vol.Element() ))
1883           {
1884             triaSplits.push_back( t012 );
1885             triaSplits.push_back( t023 );
1886             break;
1887           }
1888         }
1889       }
1890       if ( !triaSplits.empty() )
1891         hasAdjacentSplits = true;
1892     }
1893
1894     // Among variants of split method select one compliant with adjacent volumes
1895
1896     TSplitMethod method;
1897     if ( !vol.Element()->IsPoly() && !is24TetMode )
1898     {
1899       int nbVariants = 2, nbTet = 0;
1900       const int** connVariants = 0;
1901       switch ( vol.Element()->GetEntityType() )
1902       {
1903       case SMDSEntity_Hexa:
1904       case SMDSEntity_Quad_Hexa:
1905       case SMDSEntity_TriQuad_Hexa:
1906         if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1907           connVariants = theHexTo5, nbTet = 5;
1908         else
1909           connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1910         break;
1911       case SMDSEntity_Pyramid:
1912       case SMDSEntity_Quad_Pyramid:
1913         connVariants = thePyraTo2;  nbTet = 2;
1914         break;
1915       case SMDSEntity_Penta:
1916       case SMDSEntity_Quad_Penta:
1917       case SMDSEntity_BiQuad_Penta:
1918         connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1919         break;
1920       default:
1921         nbVariants = 0;
1922       }
1923       for ( int variant = 0; variant < nbVariants && method._nbSplits == 0; ++variant )
1924       {
1925         // check method compliancy with adjacent tetras,
1926         // all found splits must be among facets of tetras described by this method
1927         method = TSplitMethod( nbTet, connVariants[variant] );
1928         if ( hasAdjacentSplits && method._nbSplits > 0 )
1929         {
1930           bool facetCreated = true;
1931           for ( size_t iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1932           {
1933             list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1934             for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1935               facetCreated = method.hasFacet( *facet );
1936           }
1937           if ( !facetCreated )
1938             method = TSplitMethod(0); // incompatible method
1939         }
1940       }
1941     }
1942     if ( method._nbSplits < 1 )
1943     {
1944       // No standard method is applicable, use a generic solution:
1945       // each facet of a volume is split into triangles and
1946       // each of triangles and a volume barycenter form a tetrahedron.
1947
1948       const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1949
1950       int* connectivity = new int[ maxTetConnSize + 1 ];
1951       method._connectivity = connectivity;
1952       method._ownConn = true;
1953       method._baryNode = !isHex27; // to create central node or not
1954
1955       int connSize = 0;
1956       int baryCenInd = vol.NbNodes() - int( isHex27 );
1957       for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1958       {
1959         const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1960         const int*   nInd = vol.GetFaceNodesIndices( iF );
1961         // find common node of triangle facets of tetra to create
1962         int iCommon = 0; // index in linear numeration
1963         const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1964         if ( !triaSplits.empty() )
1965         {
1966           // by found facets
1967           const TTriangleFacet* facet = &triaSplits.front();
1968           for ( ; iCommon < nbNodes-1 ; ++iCommon )
1969             if ( facet->contains( nInd[ iQ * iCommon ]) &&
1970                  facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1971               break;
1972         }
1973         else if ( nbNodes > 3 && !is24TetMode )
1974         {
1975           // find the best method of splitting into triangles by aspect ratio
1976           SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1977           map< double, int > badness2iCommon;
1978           const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1979           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1980           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1981           {
1982             double badness = 0;
1983             for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1984             {
1985               SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon         )],
1986                                       nodes[ iQ*((iLast-1)%nbNodes)],
1987                                       nodes[ iQ*((iLast  )%nbNodes)]);
1988               badness += getBadRate( &tria, aspectRatio );
1989             }
1990             badness2iCommon.insert( make_pair( badness, iCommon ));
1991           }
1992           // use iCommon with lowest badness
1993           iCommon = badness2iCommon.begin()->second;
1994         }
1995         if ( iCommon >= nbNodes )
1996           iCommon = 0; // something wrong
1997
1998         // fill connectivity of tetrahedra based on a current face
1999         int nbTet = nbNodes - 2;
2000         if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
2001         {
2002           int faceBaryCenInd;
2003           if ( isHex27 )
2004           {
2005             faceBaryCenInd = vol.GetCenterNodeIndex( iF );
2006             method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
2007           }
2008           else
2009           {
2010             method._faceBaryNode[ iF ] = 0;
2011             faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
2012           }
2013           nbTet = nbNodes;
2014           for ( int i = 0; i < nbTet; ++i )
2015           {
2016             int i1 = i, i2 = (i+1) % nbNodes;
2017             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2018             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2019             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2020             connectivity[ connSize++ ] = faceBaryCenInd;
2021             connectivity[ connSize++ ] = baryCenInd;
2022           }
2023         }
2024         else
2025         {
2026           for ( int i = 0; i < nbTet; ++i )
2027           {
2028             int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
2029             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2030             connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
2031             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2032             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2033             connectivity[ connSize++ ] = baryCenInd;
2034           }
2035         }
2036         method._nbSplits += nbTet;
2037
2038       } // loop on volume faces
2039
2040       connectivity[ connSize++ ] = -1;
2041
2042     } // end of generic solution
2043
2044     return method;
2045   }
2046   //=======================================================================
2047   /*!
2048    * \brief return TSplitMethod to split haxhedron into prisms
2049    */
2050   //=======================================================================
2051
2052   TSplitMethod getPrismSplitMethod( SMDS_VolumeTool& vol,
2053                                     const int        methodFlags,
2054                                     const int        facetToSplit)
2055   {
2056     // order of facets in HEX according to SMDS_VolumeTool::Hexa_F :
2057     // B, T, L, B, R, F
2058     const int iF = ( facetToSplit < 2 ) ? 0 : 1 + ( facetToSplit-2 ) % 2; // [0,1,2]
2059
2060     if ( methodFlags == SMESH_MeshEditor::HEXA_TO_4_PRISMS )
2061     {
2062       static TSplitMethod to4methods[4]; // order BT, LR, FB
2063       if ( to4methods[iF]._nbSplits == 0 )
2064       {
2065         switch ( iF ) {
2066         case 0:
2067           to4methods[iF]._connectivity = theHexTo4Prisms_BT;
2068           to4methods[iF]._faceBaryNode[ 0 ] = 0;
2069           to4methods[iF]._faceBaryNode[ 1 ] = 0;
2070           break;
2071         case 1:
2072           to4methods[iF]._connectivity = theHexTo4Prisms_LR;
2073           to4methods[iF]._faceBaryNode[ 2 ] = 0;
2074           to4methods[iF]._faceBaryNode[ 4 ] = 0;
2075           break;
2076         case 2:
2077           to4methods[iF]._connectivity = theHexTo4Prisms_FB;
2078           to4methods[iF]._faceBaryNode[ 3 ] = 0;
2079           to4methods[iF]._faceBaryNode[ 5 ] = 0;
2080           break;
2081         default: return to4methods[3];
2082         }
2083         to4methods[iF]._nbSplits  = 4;
2084         to4methods[iF]._nbCorners = 6;
2085       }
2086       return to4methods[iF];
2087     }
2088     // else if ( methodFlags == HEXA_TO_2_PRISMS )
2089
2090     TSplitMethod method;
2091
2092     const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2093
2094     const int nbVariants = 2, nbSplits = 2;
2095     const int** connVariants = 0;
2096     switch ( iF ) {
2097     case 0: connVariants = theHexTo2Prisms_BT; break;
2098     case 1: connVariants = theHexTo2Prisms_LR; break;
2099     case 2: connVariants = theHexTo2Prisms_FB; break;
2100     default: return method;
2101     }
2102
2103     // look for prisms adjacent via facetToSplit and an opposite one
2104     for ( int is2nd = 0; is2nd < 2; ++is2nd )
2105     {
2106       int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2107       int nbNodes = vol.NbFaceNodes( iFacet ) / iQ;
2108       if ( nbNodes != 4 ) return method;
2109
2110       const int* nInd = vol.GetFaceNodesIndices( iFacet );
2111       TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2112       TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2113       TTriangleFacet* t;
2114       if      ( t012.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2115         t = &t012;
2116       else if ( t123.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2117         t = &t123;
2118       else
2119         continue;
2120
2121       // there are adjacent prism
2122       for ( int variant = 0; variant < nbVariants; ++variant )
2123       {
2124         // check method compliancy with adjacent prisms,
2125         // the found prism facets must be among facets of prisms described by current method
2126         method._nbSplits     = nbSplits;
2127         method._nbCorners    = 6;
2128         method._connectivity = connVariants[ variant ];
2129         if ( method.hasFacet( *t ))
2130           return method;
2131       }
2132     }
2133
2134     // No adjacent prisms. Select a variant with a best aspect ratio.
2135
2136     double badness[2] = { 0., 0. };
2137     static SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
2138     const SMDS_MeshNode** nodes = vol.GetNodes();
2139     for ( int variant = 0; variant < nbVariants; ++variant )
2140       for ( int is2nd = 0; is2nd < 2; ++is2nd )
2141       {
2142         int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2143         const int*             nInd = vol.GetFaceNodesIndices( iFacet );
2144
2145         method._connectivity = connVariants[ variant ];
2146         TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2147         TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2148         TTriangleFacet* t = ( method.hasFacet( t012 )) ? & t012 : & t123;
2149
2150         SMDS_FaceOfNodes tria ( nodes[ t->_n1 ],
2151                                 nodes[ t->_n2 ],
2152                                 nodes[ t->_n3 ] );
2153         badness[ variant ] += getBadRate( &tria, aspectRatio );
2154       }
2155     const int iBetter = ( badness[1] < badness[0] && badness[0]-badness[1] > 0.1 * badness[0] );
2156
2157     method._nbSplits     = nbSplits;
2158     method._nbCorners    = 6;
2159     method._connectivity = connVariants[ iBetter ];
2160
2161     return method;
2162   }
2163
2164   //================================================================================
2165   /*!
2166    * \brief Check if there is a tetraherdon adjacent to the given element via this facet
2167    */
2168   //================================================================================
2169
2170   bool TTriangleFacet::hasAdjacentVol( const SMDS_MeshElement*    elem,
2171                                        const SMDSAbs_GeometryType geom ) const
2172   {
2173     // find the tetrahedron including the three nodes of facet
2174     const SMDS_MeshNode* n1 = elem->GetNode(_n1);
2175     const SMDS_MeshNode* n2 = elem->GetNode(_n2);
2176     const SMDS_MeshNode* n3 = elem->GetNode(_n3);
2177     SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
2178     while ( volIt1->more() )
2179     {
2180       const SMDS_MeshElement* v = volIt1->next();
2181       if ( v->GetGeomType() != geom )
2182         continue;
2183       const int lastCornerInd = v->NbCornerNodes() - 1;
2184       if ( v->IsQuadratic() && v->GetNodeIndex( n1 ) > lastCornerInd )
2185         continue; // medium node not allowed
2186       const int ind2 = v->GetNodeIndex( n2 );
2187       if ( ind2 < 0 || lastCornerInd < ind2 )
2188         continue;
2189       const int ind3 = v->GetNodeIndex( n3 );
2190       if ( ind3 < 0 || lastCornerInd < ind3 )
2191         continue;
2192       return true;
2193     }
2194     return false;
2195   }
2196
2197   //=======================================================================
2198   /*!
2199    * \brief A key of a face of volume
2200    */
2201   //=======================================================================
2202
2203   struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
2204   {
2205     TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
2206     {
2207       TIDSortedNodeSet sortedNodes;
2208       const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2209       int nbNodes = vol.NbFaceNodes( iF );
2210       const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
2211       for ( int i = 0; i < nbNodes; i += iQ )
2212         sortedNodes.insert( fNodes[i] );
2213       TIDSortedNodeSet::iterator n = sortedNodes.begin();
2214       first.first   = (*(n++))->GetID();
2215       first.second  = (*(n++))->GetID();
2216       second.first  = (*(n++))->GetID();
2217       second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
2218     }
2219   };
2220 } // namespace
2221
2222 //=======================================================================
2223 //function : SplitVolumes
2224 //purpose  : Split volume elements into tetrahedra or prisms.
2225 //           If facet ID < 0, element is split into tetrahedra,
2226 //           else a hexahedron is split into prisms so that the given facet is
2227 //           split into triangles
2228 //=======================================================================
2229
2230 void SMESH_MeshEditor::SplitVolumes (const TFacetOfElem & theElems,
2231                                      const int            theMethodFlags)
2232 {
2233   SMDS_VolumeTool    volTool;
2234   SMESH_MesherHelper helper( *GetMesh()), fHelper(*GetMesh());
2235   fHelper.ToFixNodeParameters( true );
2236
2237   SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
2238   SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
2239
2240   SMESH_SequenceOfElemPtr newNodes, newElems;
2241
2242   // map face of volume to it's baricenrtic node
2243   map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
2244   double bc[3];
2245   vector<const SMDS_MeshElement* > splitVols;
2246
2247   TFacetOfElem::const_iterator elem2facet = theElems.begin();
2248   for ( ; elem2facet != theElems.end(); ++elem2facet )
2249   {
2250     const SMDS_MeshElement* elem = elem2facet->first;
2251     const int       facetToSplit = elem2facet->second;
2252     if ( elem->GetType() != SMDSAbs_Volume )
2253       continue;
2254     const SMDSAbs_EntityType geomType = elem->GetEntityType();
2255     if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
2256       continue;
2257
2258     if ( !volTool.Set( elem, /*ignoreCentralNodes=*/false )) continue; // strange...
2259
2260     TSplitMethod splitMethod = ( facetToSplit < 0  ?
2261                                  getTetraSplitMethod( volTool, theMethodFlags ) :
2262                                  getPrismSplitMethod( volTool, theMethodFlags, facetToSplit ));
2263     if ( splitMethod._nbSplits < 1 ) continue;
2264
2265     // find submesh to add new tetras to
2266     if ( !subMesh || !subMesh->Contains( elem ))
2267     {
2268       int shapeID = FindShape( elem );
2269       helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
2270       subMesh = GetMeshDS()->MeshElements( shapeID );
2271     }
2272     int iQ;
2273     if ( elem->IsQuadratic() )
2274     {
2275       iQ = 2;
2276       // add quadratic links to the helper
2277       for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2278       {
2279         const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
2280         int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
2281         for ( int iN = 0; iN < nbN; iN += iQ )
2282           helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
2283       }
2284       helper.SetIsQuadratic( true );
2285     }
2286     else
2287     {
2288       iQ = 1;
2289       helper.SetIsQuadratic( false );
2290     }
2291     vector<const SMDS_MeshNode*> nodes( volTool.GetNodes(),
2292                                         volTool.GetNodes() + elem->NbNodes() );
2293     helper.SetElementsOnShape( true );
2294     if ( splitMethod._baryNode )
2295     {
2296       // make a node at barycenter
2297       volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
2298       SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
2299       nodes.push_back( gcNode );
2300       newNodes.Append( gcNode );
2301     }
2302     if ( !splitMethod._faceBaryNode.empty() )
2303     {
2304       // make or find baricentric nodes of faces
2305       map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
2306       for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
2307       {
2308         map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
2309           volFace2BaryNode.insert
2310           ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
2311         if ( !f_n->second )
2312         {
2313           volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
2314           newNodes.Append( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
2315         }
2316         nodes.push_back( iF_n->second = f_n->second );
2317       }
2318     }
2319
2320     // make new volumes
2321     splitVols.resize( splitMethod._nbSplits ); // splits of a volume
2322     const int* volConn = splitMethod._connectivity;
2323     if ( splitMethod._nbCorners == 4 ) // tetra
2324       for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2325         newElems.Append( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2326                                                             nodes[ volConn[1] ],
2327                                                             nodes[ volConn[2] ],
2328                                                             nodes[ volConn[3] ]));
2329     else // prisms
2330       for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2331         newElems.Append( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2332                                                             nodes[ volConn[1] ],
2333                                                             nodes[ volConn[2] ],
2334                                                             nodes[ volConn[3] ],
2335                                                             nodes[ volConn[4] ],
2336                                                             nodes[ volConn[5] ]));
2337
2338     ReplaceElemInGroups( elem, splitVols, GetMeshDS() );
2339
2340     // Split faces on sides of the split volume
2341
2342     const SMDS_MeshNode** volNodes = volTool.GetNodes();
2343     for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2344     {
2345       const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
2346       if ( nbNodes < 4 ) continue;
2347
2348       // find an existing face
2349       vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
2350                                            volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
2351       while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
2352                                                                        /*noMedium=*/false))
2353       {
2354         // make triangles
2355         helper.SetElementsOnShape( false );
2356         vector< const SMDS_MeshElement* > triangles;
2357
2358         // find submesh to add new triangles in
2359         if ( !fSubMesh || !fSubMesh->Contains( face ))
2360         {
2361           int shapeID = FindShape( face );
2362           fSubMesh = GetMeshDS()->MeshElements( shapeID );
2363         }
2364         map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
2365         if ( iF_n != splitMethod._faceBaryNode.end() )
2366         {
2367           const SMDS_MeshNode *baryNode = iF_n->second;
2368           for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
2369           {
2370             const SMDS_MeshNode* n1 = fNodes[iN];
2371             const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
2372             const SMDS_MeshNode *n3 = baryNode;
2373             if ( !volTool.IsFaceExternal( iF ))
2374               swap( n2, n3 );
2375             triangles.push_back( helper.AddFace( n1,n2,n3 ));
2376           }
2377           if ( fSubMesh ) // update position of the bary node on geometry
2378           {
2379             if ( subMesh )
2380               subMesh->RemoveNode( baryNode, false );
2381             GetMeshDS()->SetNodeOnFace( baryNode, fSubMesh->GetID() );
2382             const TopoDS_Shape& s = GetMeshDS()->IndexToShape( fSubMesh->GetID() );
2383             if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE )
2384             {
2385               fHelper.SetSubShape( s );
2386               gp_XY uv( 1e100, 1e100 );
2387               double distXYZ[4];
2388               if ( !fHelper.CheckNodeUV( TopoDS::Face( s ), baryNode,
2389                                         uv, /*tol=*/1e-7, /*force=*/true, distXYZ ) &&
2390                    uv.X() < 1e100 )
2391               {
2392                 // node is too far from the surface
2393                 GetMeshDS()->MoveNode( baryNode, distXYZ[1], distXYZ[2], distXYZ[3] );
2394                 const_cast<SMDS_MeshNode*>( baryNode )->SetPosition
2395                   ( SMDS_PositionPtr( new SMDS_FacePosition( uv.X(), uv.Y() )));
2396               }
2397             }
2398           }
2399         }
2400         else
2401         {
2402           // among possible triangles create ones described by split method
2403           const int* nInd = volTool.GetFaceNodesIndices( iF );
2404           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2405           int iCom = 0; // common node of triangle faces to split into
2406           list< TTriangleFacet > facets;
2407           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
2408           {
2409             TTriangleFacet t012( nInd[ iQ * ( iCom                )],
2410                                  nInd[ iQ * ( (iCom+1)%nbNodes )],
2411                                  nInd[ iQ * ( (iCom+2)%nbNodes )]);
2412             TTriangleFacet t023( nInd[ iQ * ( iCom                )],
2413                                  nInd[ iQ * ( (iCom+2)%nbNodes )],
2414                                  nInd[ iQ * ( (iCom+3)%nbNodes )]);
2415             if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
2416             {
2417               facets.push_back( t012 );
2418               facets.push_back( t023 );
2419               for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
2420                 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom             )],
2421                                                   nInd[ iQ * ((iLast-1)%nbNodes )],
2422                                                   nInd[ iQ * ((iLast  )%nbNodes )]));
2423               break;
2424             }
2425           }
2426           list< TTriangleFacet >::iterator facet = facets.begin();
2427           if ( facet == facets.end() )
2428             break;
2429           for ( ; facet != facets.end(); ++facet )
2430           {
2431             if ( !volTool.IsFaceExternal( iF ))
2432               swap( facet->_n2, facet->_n3 );
2433             triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
2434                                                  volNodes[ facet->_n2 ],
2435                                                  volNodes[ facet->_n3 ]));
2436           }
2437         }
2438         for ( size_t i = 0; i < triangles.size(); ++i )
2439         {
2440           if ( !triangles[ i ]) continue;
2441           if ( fSubMesh )
2442             fSubMesh->AddElement( triangles[ i ]);
2443           newElems.Append( triangles[ i ]);
2444         }
2445         ReplaceElemInGroups( face, triangles, GetMeshDS() );
2446         GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2447
2448       } // while a face based on facet nodes exists
2449     } // loop on volume faces to split them into triangles
2450
2451     GetMeshDS()->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2452
2453     if ( geomType == SMDSEntity_TriQuad_Hexa )
2454     {
2455       // remove medium nodes that could become free
2456       for ( int i = 20; i < volTool.NbNodes(); ++i )
2457         if ( volNodes[i]->NbInverseElements() == 0 )
2458           GetMeshDS()->RemoveNode( volNodes[i] );
2459     }
2460   } // loop on volumes to split
2461
2462   myLastCreatedNodes = newNodes;
2463   myLastCreatedElems = newElems;
2464 }
2465
2466 //=======================================================================
2467 //function : GetHexaFacetsToSplit
2468 //purpose  : For hexahedra that will be split into prisms, finds facets to
2469 //           split into triangles. Only hexahedra adjacent to the one closest
2470 //           to theFacetNormal.Location() are returned.
2471 //param [in,out] theHexas - the hexahedra
2472 //param [in]     theFacetNormal - facet normal
2473 //param [out]    theFacets - the hexahedra and found facet IDs
2474 //=======================================================================
2475
2476 void SMESH_MeshEditor::GetHexaFacetsToSplit( TIDSortedElemSet& theHexas,
2477                                              const gp_Ax1&     theFacetNormal,
2478                                              TFacetOfElem &    theFacets)
2479 {
2480   #define THIS_METHOD "SMESH_MeshEditor::GetHexaFacetsToSplit(): "
2481
2482   // Find a hexa closest to the location of theFacetNormal
2483
2484   const SMDS_MeshElement* startHex;
2485   {
2486     // get SMDS_ElemIteratorPtr on theHexas
2487     typedef const SMDS_MeshElement*                                      TValue;
2488     typedef TIDSortedElemSet::iterator                                   TSetIterator;
2489     typedef SMDS::SimpleAccessor<TValue,TSetIterator>                    TAccesor;
2490     typedef SMDS_MeshElement::GeomFilter                                 TFilter;
2491     typedef SMDS_SetIterator < TValue, TSetIterator, TAccesor, TFilter > TElemSetIter;
2492     SMDS_ElemIteratorPtr elemIt = SMDS_ElemIteratorPtr
2493       ( new TElemSetIter( theHexas.begin(),
2494                           theHexas.end(),
2495                           SMDS_MeshElement::GeomFilter( SMDSGeom_HEXA )));
2496
2497     SMESH_ElementSearcher* searcher =
2498       SMESH_MeshAlgos::GetElementSearcher( *myMesh->GetMeshDS(), elemIt );
2499
2500     startHex = searcher->FindClosestTo( theFacetNormal.Location(), SMDSAbs_Volume );
2501
2502     delete searcher;
2503
2504     if ( !startHex )
2505       throw SALOME_Exception( THIS_METHOD "startHex not found");
2506   }
2507
2508   // Select a facet of startHex by theFacetNormal
2509
2510   SMDS_VolumeTool vTool( startHex );
2511   double norm[3], dot, maxDot = 0;
2512   int facetID = -1;
2513   for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2514     if ( vTool.GetFaceNormal( iF, norm[0], norm[1], norm[2] ))
2515     {
2516       dot = Abs( theFacetNormal.Direction().Dot( gp_Dir( norm[0], norm[1], norm[2] )));
2517       if ( dot > maxDot )
2518       {
2519         facetID = iF;
2520         maxDot = dot;
2521       }
2522     }
2523   if ( facetID < 0 )
2524     throw SALOME_Exception( THIS_METHOD "facet of startHex not found");
2525
2526   // Fill theFacets starting from facetID of startHex
2527
2528   // facets used for searching of volumes adjacent to already treated ones
2529   typedef pair< TFacetOfElem::iterator, int > TElemFacets;
2530   typedef map< TVolumeFaceKey, TElemFacets  > TFacetMap;
2531   TFacetMap facetsToCheck;
2532
2533   set<const SMDS_MeshNode*> facetNodes;
2534   const SMDS_MeshElement*   curHex;
2535
2536   const bool allHex = ((int) theHexas.size() == myMesh->NbHexas() );
2537
2538   while ( startHex )
2539   {
2540     // move in two directions from startHex via facetID
2541     for ( int is2nd = 0; is2nd < 2; ++is2nd )
2542     {
2543       curHex       = startHex;
2544       int curFacet = facetID;
2545       if ( is2nd ) // do not treat startHex twice
2546       {
2547         vTool.Set( curHex );
2548         if ( vTool.IsFreeFace( curFacet, &curHex ))
2549         {
2550           curHex = 0;
2551         }
2552         else
2553         {
2554           vTool.GetFaceNodes( curFacet, facetNodes );
2555           vTool.Set( curHex );
2556           curFacet = vTool.GetFaceIndex( facetNodes );
2557         }
2558       }
2559       while ( curHex )
2560       {
2561         // store a facet to split
2562         if ( curHex->GetGeomType() != SMDSGeom_HEXA )
2563         {
2564           theFacets.insert( make_pair( curHex, -1 ));
2565           break;
2566         }
2567         if ( !allHex && !theHexas.count( curHex ))
2568           break;
2569
2570         pair< TFacetOfElem::iterator, bool > facetIt2isNew =
2571           theFacets.insert( make_pair( curHex, curFacet ));
2572         if ( !facetIt2isNew.second )
2573           break;
2574
2575         // remember not-to-split facets in facetsToCheck
2576         int oppFacet = vTool.GetOppFaceIndexOfHex( curFacet );
2577         for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2578         {
2579           if ( iF == curFacet && iF == oppFacet )
2580             continue;
2581           TVolumeFaceKey facetKey ( vTool, iF );
2582           TElemFacets    elemFacet( facetIt2isNew.first, iF );
2583           pair< TFacetMap::iterator, bool > it2isnew =
2584             facetsToCheck.insert( make_pair( facetKey, elemFacet ));
2585           if ( !it2isnew.second )
2586             facetsToCheck.erase( it2isnew.first ); // adjacent hex already checked
2587         }
2588         // pass to a volume adjacent via oppFacet
2589         if ( vTool.IsFreeFace( oppFacet, &curHex ))
2590         {
2591           curHex = 0;
2592         }
2593         else
2594         {
2595           // get a new curFacet
2596           vTool.GetFaceNodes( oppFacet, facetNodes );
2597           vTool.Set( curHex );
2598           curFacet = vTool.GetFaceIndex( facetNodes, /*hint=*/curFacet );
2599         }
2600       }
2601     } // move in two directions from startHex via facetID
2602
2603     // Find a new startHex by facetsToCheck
2604
2605     startHex = 0;
2606     facetID  = -1;
2607     TFacetMap::iterator fIt = facetsToCheck.begin();
2608     while ( !startHex && fIt != facetsToCheck.end() )
2609     {
2610       const TElemFacets&  elemFacets = fIt->second;
2611       const SMDS_MeshElement*    hex = elemFacets.first->first;
2612       int                 splitFacet = elemFacets.first->second;
2613       int               lateralFacet = elemFacets.second;
2614       facetsToCheck.erase( fIt );
2615       fIt = facetsToCheck.begin();
2616
2617       vTool.Set( hex );
2618       if ( vTool.IsFreeFace( lateralFacet, &curHex ) || 
2619            curHex->GetGeomType() != SMDSGeom_HEXA )
2620         continue;
2621       if ( !allHex && !theHexas.count( curHex ))
2622         continue;
2623
2624       startHex = curHex;
2625
2626       // find a facet of startHex to split
2627
2628       set<const SMDS_MeshNode*> lateralNodes;
2629       vTool.GetFaceNodes( lateralFacet, lateralNodes );
2630       vTool.GetFaceNodes( splitFacet,   facetNodes );
2631       int oppLateralFacet = vTool.GetOppFaceIndexOfHex( lateralFacet );
2632       vTool.Set( startHex );
2633       lateralFacet = vTool.GetFaceIndex( lateralNodes, oppLateralFacet );
2634
2635       // look for a facet of startHex having common nodes with facetNodes
2636       // but not lateralFacet
2637       for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2638       {
2639         if ( iF == lateralFacet )
2640           continue;
2641         int nbCommonNodes = 0;
2642         const SMDS_MeshNode** nn = vTool.GetFaceNodes( iF );
2643         for ( int iN = 0, nbN = vTool.NbFaceNodes( iF ); iN < nbN; ++iN )
2644           nbCommonNodes += facetNodes.count( nn[ iN ]);
2645
2646         if ( nbCommonNodes >= 2 )
2647         {
2648           facetID = iF;
2649           break;
2650         }
2651       }
2652       if ( facetID < 0 )
2653         throw SALOME_Exception( THIS_METHOD "facet of a new startHex not found");
2654     }
2655   } //   while ( startHex )
2656
2657   return;
2658 }
2659
2660 namespace
2661 {
2662   //================================================================================
2663   /*!
2664    * \brief Selects nodes of several elements according to a given interlace
2665    *  \param [in] srcNodes - nodes to select from
2666    *  \param [out] tgtNodesVec - array of nodes of several elements to fill in
2667    *  \param [in] interlace - indices of nodes for all elements
2668    *  \param [in] nbElems - nb of elements
2669    *  \param [in] nbNodes - nb of nodes in each element
2670    *  \param [in] mesh - the mesh
2671    *  \param [out] elemQueue - a list to push elements found by the selected nodes
2672    *  \param [in] type - type of elements to look for
2673    */
2674   //================================================================================
2675
2676   void selectNodes( const vector< const SMDS_MeshNode* >& srcNodes,
2677                     vector< const SMDS_MeshNode* >*       tgtNodesVec,
2678                     const int*                            interlace,
2679                     const int                             nbElems,
2680                     const int                             nbNodes,
2681                     SMESHDS_Mesh*                         mesh = 0,
2682                     list< const SMDS_MeshElement* >*      elemQueue=0,
2683                     SMDSAbs_ElementType                   type=SMDSAbs_All)
2684   {
2685     for ( int iE = 0; iE < nbElems; ++iE )
2686     {
2687       vector< const SMDS_MeshNode* >& elemNodes = tgtNodesVec[iE];
2688       const int*                         select = & interlace[iE*nbNodes];
2689       elemNodes.resize( nbNodes );
2690       for ( int iN = 0; iN < nbNodes; ++iN )
2691         elemNodes[iN] = srcNodes[ select[ iN ]];
2692     }
2693     const SMDS_MeshElement* e;
2694     if ( elemQueue )
2695       for ( int iE = 0; iE < nbElems; ++iE )
2696         if (( e = mesh->FindElement( tgtNodesVec[iE], type, /*noMedium=*/false)))
2697           elemQueue->push_back( e );
2698   }
2699 }
2700
2701 //=======================================================================
2702 /*
2703  * Split bi-quadratic elements into linear ones without creation of additional nodes
2704  *   - bi-quadratic triangle will be split into 3 linear quadrangles;
2705  *   - bi-quadratic quadrangle will be split into 4 linear quadrangles;
2706  *   - tri-quadratic hexahedron will be split into 8 linear hexahedra;
2707  *   Quadratic elements of lower dimension  adjacent to the split bi-quadratic element
2708  *   will be split in order to keep the mesh conformal.
2709  *  \param elems - elements to split
2710  */
2711 //=======================================================================
2712
2713 void SMESH_MeshEditor::SplitBiQuadraticIntoLinear(TIDSortedElemSet& theElems)
2714 {
2715   vector< const SMDS_MeshNode* > elemNodes(27), subNodes[12], splitNodes[8];
2716   vector<const SMDS_MeshElement* > splitElems;
2717   list< const SMDS_MeshElement* > elemQueue;
2718   list< const SMDS_MeshElement* >::iterator elemIt;
2719
2720   SMESHDS_Mesh * mesh = GetMeshDS();
2721   ElemFeatures *elemType, hexaType(SMDSAbs_Volume), quadType(SMDSAbs_Face), segType(SMDSAbs_Edge);
2722   int nbElems, nbNodes;
2723
2724   TIDSortedElemSet::iterator elemSetIt = theElems.begin();
2725   for ( ; elemSetIt != theElems.end(); ++elemSetIt )
2726   {
2727     elemQueue.clear();
2728     elemQueue.push_back( *elemSetIt );
2729     for ( elemIt = elemQueue.begin(); elemIt != elemQueue.end(); ++elemIt )
2730     {
2731       const SMDS_MeshElement* elem = *elemIt;
2732       switch( elem->GetEntityType() )
2733       {
2734       case SMDSEntity_TriQuad_Hexa: // HEX27
2735       {
2736         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2737         nbElems  = nbNodes = 8;
2738         elemType = & hexaType;
2739
2740         // get nodes for new elements
2741         static int vInd[8][8] = {{ 0,8,20,11,   16,21,26,24 },
2742                                  { 1,9,20,8,    17,22,26,21 },
2743                                  { 2,10,20,9,   18,23,26,22 },
2744                                  { 3,11,20,10,  19,24,26,23 },
2745                                  { 16,21,26,24, 4,12,25,15  },
2746                                  { 17,22,26,21, 5,13,25,12  },
2747                                  { 18,23,26,22, 6,14,25,13  },
2748                                  { 19,24,26,23, 7,15,25,14  }};
2749         selectNodes( elemNodes, & splitNodes[0], &vInd[0][0], nbElems, nbNodes );
2750
2751         // add boundary faces to elemQueue
2752         static int fInd[6][9] = {{ 0,1,2,3, 8,9,10,11,   20 },
2753                                  { 4,5,6,7, 12,13,14,15, 25 },
2754                                  { 0,1,5,4, 8,17,12,16,  21 },
2755                                  { 1,2,6,5, 9,18,13,17,  22 },
2756                                  { 2,3,7,6, 10,19,14,18, 23 },
2757                                  { 3,0,4,7, 11,16,15,19, 24 }};
2758         selectNodes( elemNodes, & subNodes[0], &fInd[0][0], 6,9, mesh, &elemQueue, SMDSAbs_Face );
2759
2760         // add boundary segments to elemQueue
2761         static int eInd[12][3] = {{ 0,1,8 }, { 1,2,9 }, { 2,3,10 }, { 3,0,11 },
2762                                   { 4,5,12}, { 5,6,13}, { 6,7,14 }, { 7,4,15 },
2763                                   { 0,4,16}, { 1,5,17}, { 2,6,18 }, { 3,7,19 }};
2764         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 12,3, mesh, &elemQueue, SMDSAbs_Edge );
2765         break;
2766       }
2767       case SMDSEntity_BiQuad_Triangle: // TRIA7
2768       {
2769         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2770         nbElems = 3;
2771         nbNodes = 4;
2772         elemType = & quadType;
2773
2774         // get nodes for new elements
2775         static int fInd[3][4] = {{ 0,3,6,5 }, { 1,4,6,3 }, { 2,5,6,4 }};
2776         selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2777
2778         // add boundary segments to elemQueue
2779         static int eInd[3][3] = {{ 0,1,3 }, { 1,2,4 }, { 2,0,5 }};
2780         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 3,3, mesh, &elemQueue, SMDSAbs_Edge );
2781         break;
2782       }
2783       case SMDSEntity_BiQuad_Quadrangle: // QUAD9
2784       {
2785         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2786         nbElems = 4;
2787         nbNodes = 4;
2788         elemType = & quadType;
2789
2790         // get nodes for new elements
2791         static int fInd[4][4] = {{ 0,4,8,7 }, { 1,5,8,4 }, { 2,6,8,5 }, { 3,7,8,6 }};
2792         selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2793
2794         // add boundary segments to elemQueue
2795         static int eInd[4][3] = {{ 0,1,4 }, { 1,2,5 }, { 2,3,6 }, { 3,0,7 }};
2796         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 4,3, mesh, &elemQueue, SMDSAbs_Edge );
2797         break;
2798       }
2799       case SMDSEntity_Quad_Edge:
2800       {
2801         if ( elemIt == elemQueue.begin() )
2802           continue; // an elem is in theElems
2803         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2804         nbElems = 2;
2805         nbNodes = 2;
2806         elemType = & segType;
2807
2808         // get nodes for new elements
2809         static int eInd[2][2] = {{ 0,2 }, { 2,1 }};
2810         selectNodes( elemNodes, & splitNodes[0], &eInd[0][0], nbElems, nbNodes );
2811         break;
2812       }
2813       default: continue;
2814       } // switch( elem->GetEntityType() )
2815
2816       // Create new elements
2817
2818       SMESHDS_SubMesh* subMesh = mesh->MeshElements( elem->getshapeId() );
2819
2820       splitElems.clear();
2821
2822       //elemType->SetID( elem->GetID() ); // create an elem with the same ID as a removed one
2823       mesh->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2824       //splitElems.push_back( AddElement( splitNodes[ 0 ], *elemType ));
2825       //elemType->SetID( -1 );
2826
2827       for ( int iE = 0; iE < nbElems; ++iE )
2828         splitElems.push_back( AddElement( splitNodes[ iE ], *elemType ));
2829
2830
2831       ReplaceElemInGroups( elem, splitElems, mesh );
2832
2833       if ( subMesh )
2834         for ( size_t i = 0; i < splitElems.size(); ++i )
2835           subMesh->AddElement( splitElems[i] );
2836     }
2837   }
2838 }
2839
2840 //=======================================================================
2841 //function : AddToSameGroups
2842 //purpose  : add elemToAdd to the groups the elemInGroups belongs to
2843 //=======================================================================
2844
2845 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
2846                                         const SMDS_MeshElement* elemInGroups,
2847                                         SMESHDS_Mesh *          aMesh)
2848 {
2849   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2850   if (!groups.empty()) {
2851     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2852     for ( ; grIt != groups.end(); grIt++ ) {
2853       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2854       if ( group && group->Contains( elemInGroups ))
2855         group->SMDSGroup().Add( elemToAdd );
2856     }
2857   }
2858 }
2859
2860
2861 //=======================================================================
2862 //function : RemoveElemFromGroups
2863 //purpose  : Remove removeelem to the groups the elemInGroups belongs to
2864 //=======================================================================
2865 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2866                                              SMESHDS_Mesh *          aMesh)
2867 {
2868   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2869   if (!groups.empty())
2870   {
2871     set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2872     for (; GrIt != groups.end(); GrIt++)
2873     {
2874       SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2875       if (!grp || grp->IsEmpty()) continue;
2876       grp->SMDSGroup().Remove(removeelem);
2877     }
2878   }
2879 }
2880
2881 //================================================================================
2882 /*!
2883  * \brief Replace elemToRm by elemToAdd in the all groups
2884  */
2885 //================================================================================
2886
2887 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2888                                             const SMDS_MeshElement* elemToAdd,
2889                                             SMESHDS_Mesh *          aMesh)
2890 {
2891   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2892   if (!groups.empty()) {
2893     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2894     for ( ; grIt != groups.end(); grIt++ ) {
2895       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2896       if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2897         group->SMDSGroup().Add( elemToAdd );
2898     }
2899   }
2900 }
2901
2902 //================================================================================
2903 /*!
2904  * \brief Replace elemToRm by elemToAdd in the all groups
2905  */
2906 //================================================================================
2907
2908 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement*                elemToRm,
2909                                             const vector<const SMDS_MeshElement*>& elemToAdd,
2910                                             SMESHDS_Mesh *                         aMesh)
2911 {
2912   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2913   if (!groups.empty())
2914   {
2915     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2916     for ( ; grIt != groups.end(); grIt++ ) {
2917       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2918       if ( group && group->SMDSGroup().Remove( elemToRm ) )
2919         for ( size_t i = 0; i < elemToAdd.size(); ++i )
2920           group->SMDSGroup().Add( elemToAdd[ i ] );
2921     }
2922   }
2923 }
2924
2925 //=======================================================================
2926 //function : QuadToTri
2927 //purpose  : Cut quadrangles into triangles.
2928 //           theCrit is used to select a diagonal to cut
2929 //=======================================================================
2930
2931 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2932                                   const bool         the13Diag)
2933 {
2934   myLastCreatedElems.Clear();
2935   myLastCreatedNodes.Clear();
2936
2937   SMESHDS_Mesh * aMesh = GetMeshDS();
2938
2939   Handle(Geom_Surface) surface;
2940   SMESH_MesherHelper   helper( *GetMesh() );
2941
2942   TIDSortedElemSet::iterator itElem;
2943   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
2944   {
2945     const SMDS_MeshElement* elem = *itElem;
2946     if ( !elem || elem->GetGeomType() != SMDSGeom_QUADRANGLE )
2947       continue;
2948
2949     if ( elem->NbNodes() == 4 ) {
2950       // retrieve element nodes
2951       const SMDS_MeshNode* aNodes [4];
2952       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2953       int i = 0;
2954       while ( itN->more() )
2955         aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2956
2957       int aShapeId = FindShape( elem );
2958       const SMDS_MeshElement* newElem1 = 0;
2959       const SMDS_MeshElement* newElem2 = 0;
2960       if ( the13Diag ) {
2961         newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2962         newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2963       }
2964       else {
2965         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2966         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2967       }
2968       myLastCreatedElems.Append(newElem1);
2969       myLastCreatedElems.Append(newElem2);
2970       // put a new triangle on the same shape and add to the same groups
2971       if ( aShapeId )
2972       {
2973         aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2974         aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2975       }
2976       AddToSameGroups( newElem1, elem, aMesh );
2977       AddToSameGroups( newElem2, elem, aMesh );
2978       aMesh->RemoveElement( elem );
2979     }
2980
2981     // Quadratic quadrangle
2982
2983     else if ( elem->NbNodes() >= 8 )
2984     {
2985       // get surface elem is on
2986       int aShapeId = FindShape( elem );
2987       if ( aShapeId != helper.GetSubShapeID() ) {
2988         surface.Nullify();
2989         TopoDS_Shape shape;
2990         if ( aShapeId > 0 )
2991           shape = aMesh->IndexToShape( aShapeId );
2992         if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
2993           TopoDS_Face face = TopoDS::Face( shape );
2994           surface = BRep_Tool::Surface( face );
2995           if ( !surface.IsNull() )
2996             helper.SetSubShape( shape );
2997         }
2998       }
2999
3000       const SMDS_MeshNode* aNodes [9]; aNodes[8] = 0;
3001       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3002       for ( int i = 0; itN->more(); ++i )
3003         aNodes[ i ] = static_cast<const SMDS_MeshNode*>( itN->next() );
3004
3005       const SMDS_MeshNode* centrNode = aNodes[8];
3006       if ( centrNode == 0 )
3007       {
3008         centrNode = helper.GetCentralNode( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3009                                            aNodes[4], aNodes[5], aNodes[6], aNodes[7],
3010                                            surface.IsNull() );
3011         myLastCreatedNodes.Append(centrNode);
3012       }
3013
3014       // create a new element
3015       const SMDS_MeshElement* newElem1 = 0;
3016       const SMDS_MeshElement* newElem2 = 0;
3017       if ( the13Diag ) {
3018         newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
3019                                   aNodes[6], aNodes[7], centrNode );
3020         newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
3021                                   centrNode, aNodes[4], aNodes[5] );
3022       }
3023       else {
3024         newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
3025                                   aNodes[7], aNodes[4], centrNode );
3026         newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
3027                                   centrNode, aNodes[5], aNodes[6] );
3028       }
3029       myLastCreatedElems.Append(newElem1);
3030       myLastCreatedElems.Append(newElem2);
3031       // put a new triangle on the same shape and add to the same groups
3032       if ( aShapeId )
3033       {
3034         aMesh->SetMeshElementOnShape( newElem1, aShapeId );
3035         aMesh->SetMeshElementOnShape( newElem2, aShapeId );
3036       }
3037       AddToSameGroups( newElem1, elem, aMesh );
3038       AddToSameGroups( newElem2, elem, aMesh );
3039       aMesh->RemoveElement( elem );
3040     }
3041   }
3042
3043   return true;
3044 }
3045
3046 //=======================================================================
3047 //function : getAngle
3048 //purpose  :
3049 //=======================================================================
3050
3051 double getAngle(const SMDS_MeshElement * tr1,
3052                 const SMDS_MeshElement * tr2,
3053                 const SMDS_MeshNode *    n1,
3054                 const SMDS_MeshNode *    n2)
3055 {
3056   double angle = 2. * M_PI; // bad angle
3057
3058   // get normals
3059   SMESH::Controls::TSequenceOfXYZ P1, P2;
3060   if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
3061        !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
3062     return angle;
3063   gp_Vec N1,N2;
3064   if(!tr1->IsQuadratic())
3065     N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
3066   else
3067     N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
3068   if ( N1.SquareMagnitude() <= gp::Resolution() )
3069     return angle;
3070   if(!tr2->IsQuadratic())
3071     N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
3072   else
3073     N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
3074   if ( N2.SquareMagnitude() <= gp::Resolution() )
3075     return angle;
3076
3077   // find the first diagonal node n1 in the triangles:
3078   // take in account a diagonal link orientation
3079   const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
3080   for ( int t = 0; t < 2; t++ ) {
3081     SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
3082     int i = 0, iDiag = -1;
3083     while ( it->more()) {
3084       const SMDS_MeshElement *n = it->next();
3085       if ( n == n1 || n == n2 ) {
3086         if ( iDiag < 0)
3087           iDiag = i;
3088         else {
3089           if ( i - iDiag == 1 )
3090             nFirst[ t ] = ( n == n1 ? n2 : n1 );
3091           else
3092             nFirst[ t ] = n;
3093           break;
3094         }
3095       }
3096       i++;
3097     }
3098   }
3099   if ( nFirst[ 0 ] == nFirst[ 1 ] )
3100     N2.Reverse();
3101
3102   angle = N1.Angle( N2 );
3103   //SCRUTE( angle );
3104   return angle;
3105 }
3106
3107 // =================================================
3108 // class generating a unique ID for a pair of nodes
3109 // and able to return nodes by that ID
3110 // =================================================
3111 class LinkID_Gen {
3112 public:
3113
3114   LinkID_Gen( const SMESHDS_Mesh* theMesh )
3115     :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
3116   {}
3117
3118   long GetLinkID (const SMDS_MeshNode * n1,
3119                   const SMDS_MeshNode * n2) const
3120   {
3121     return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
3122   }
3123
3124   bool GetNodes (const long             theLinkID,
3125                  const SMDS_MeshNode* & theNode1,
3126                  const SMDS_MeshNode* & theNode2) const
3127   {
3128     theNode1 = myMesh->FindNode( theLinkID / myMaxID );
3129     if ( !theNode1 ) return false;
3130     theNode2 = myMesh->FindNode( theLinkID % myMaxID );
3131     if ( !theNode2 ) return false;
3132     return true;
3133   }
3134
3135 private:
3136   LinkID_Gen();
3137   const SMESHDS_Mesh* myMesh;
3138   long                myMaxID;
3139 };
3140
3141
3142 //=======================================================================
3143 //function : TriToQuad
3144 //purpose  : Fuse neighbour triangles into quadrangles.
3145 //           theCrit is used to select a neighbour to fuse with.
3146 //           theMaxAngle is a max angle between element normals at which
3147 //           fusion is still performed.
3148 //=======================================================================
3149
3150 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet &                   theElems,
3151                                   SMESH::Controls::NumericalFunctorPtr theCrit,
3152                                   const double                         theMaxAngle)
3153 {
3154   myLastCreatedElems.Clear();
3155   myLastCreatedNodes.Clear();
3156
3157   if ( !theCrit.get() )
3158     return false;
3159
3160   SMESHDS_Mesh * aMesh = GetMeshDS();
3161
3162   // Prepare data for algo: build
3163   // 1. map of elements with their linkIDs
3164   // 2. map of linkIDs with their elements
3165
3166   map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
3167   map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
3168   map< const SMDS_MeshElement*, set< SMESH_TLink > >  mapEl_setLi;
3169   map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
3170
3171   TIDSortedElemSet::iterator itElem;
3172   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
3173   {
3174     const SMDS_MeshElement* elem = *itElem;
3175     if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
3176     bool IsTria = ( elem->NbCornerNodes()==3 );
3177     if (!IsTria) continue;
3178
3179     // retrieve element nodes
3180     const SMDS_MeshNode* aNodes [4];
3181     SMDS_NodeIteratorPtr itN = elem->nodeIterator();
3182     int i = 0;
3183     while ( i < 3 )
3184       aNodes[ i++ ] = itN->next();
3185     aNodes[ 3 ] = aNodes[ 0 ];
3186
3187     // fill maps
3188     for ( i = 0; i < 3; i++ ) {
3189       SMESH_TLink link( aNodes[i], aNodes[i+1] );
3190       // check if elements sharing a link can be fused
3191       itLE = mapLi_listEl.find( link );
3192       if ( itLE != mapLi_listEl.end() ) {
3193         if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
3194           continue;
3195         const SMDS_MeshElement* elem2 = (*itLE).second.front();
3196         //if ( FindShape( elem ) != FindShape( elem2 ))
3197         //  continue; // do not fuse triangles laying on different shapes
3198         if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
3199           continue; // avoid making badly shaped quads
3200         (*itLE).second.push_back( elem );
3201       }
3202       else {
3203         mapLi_listEl[ link ].push_back( elem );
3204       }
3205       mapEl_setLi [ elem ].insert( link );
3206     }
3207   }
3208   // Clean the maps from the links shared by a sole element, ie
3209   // links to which only one element is bound in mapLi_listEl
3210
3211   for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
3212     int nbElems = (*itLE).second.size();
3213     if ( nbElems < 2  ) {
3214       const SMDS_MeshElement* elem = (*itLE).second.front();
3215       SMESH_TLink link = (*itLE).first;
3216       mapEl_setLi[ elem ].erase( link );
3217       if ( mapEl_setLi[ elem ].empty() )
3218         mapEl_setLi.erase( elem );
3219     }
3220   }
3221
3222   // Algo: fuse triangles into quadrangles
3223
3224   while ( ! mapEl_setLi.empty() ) {
3225     // Look for the start element:
3226     // the element having the least nb of shared links
3227     const SMDS_MeshElement* startElem = 0;
3228     int minNbLinks = 4;
3229     for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
3230       int nbLinks = (*itEL).second.size();
3231       if ( nbLinks < minNbLinks ) {
3232         startElem = (*itEL).first;
3233         minNbLinks = nbLinks;
3234         if ( minNbLinks == 1 )
3235           break;
3236       }
3237     }
3238
3239     // search elements to fuse starting from startElem or links of elements
3240     // fused earlyer - startLinks
3241     list< SMESH_TLink > startLinks;
3242     while ( startElem || !startLinks.empty() ) {
3243       while ( !startElem && !startLinks.empty() ) {
3244         // Get an element to start, by a link
3245         SMESH_TLink linkId = startLinks.front();
3246         startLinks.pop_front();
3247         itLE = mapLi_listEl.find( linkId );
3248         if ( itLE != mapLi_listEl.end() ) {
3249           list< const SMDS_MeshElement* > & listElem = (*itLE).second;
3250           list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
3251           for ( ; itE != listElem.end() ; itE++ )
3252             if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
3253               startElem = (*itE);
3254           mapLi_listEl.erase( itLE );
3255         }
3256       }
3257
3258       if ( startElem ) {
3259         // Get candidates to be fused
3260         const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
3261         const SMESH_TLink *link12 = 0, *link13 = 0;
3262         startElem = 0;
3263         ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
3264         set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
3265         ASSERT( !setLi.empty() );
3266         set< SMESH_TLink >::iterator itLi;
3267         for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
3268         {
3269           const SMESH_TLink & link = (*itLi);
3270           itLE = mapLi_listEl.find( link );
3271           if ( itLE == mapLi_listEl.end() )
3272             continue;
3273
3274           const SMDS_MeshElement* elem = (*itLE).second.front();
3275           if ( elem == tr1 )
3276             elem = (*itLE).second.back();
3277           mapLi_listEl.erase( itLE );
3278           if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
3279             continue;
3280           if ( tr2 ) {
3281             tr3 = elem;
3282             link13 = &link;
3283           }
3284           else {
3285             tr2 = elem;
3286             link12 = &link;
3287           }
3288
3289           // add other links of elem to list of links to re-start from
3290           set< SMESH_TLink >& links = mapEl_setLi[ elem ];
3291           set< SMESH_TLink >::iterator it;
3292           for ( it = links.begin(); it != links.end(); it++ ) {
3293             const SMESH_TLink& link2 = (*it);
3294             if ( link2 != link )
3295               startLinks.push_back( link2 );
3296           }
3297         }
3298
3299         // Get nodes of possible quadrangles
3300         const SMDS_MeshNode *n12 [4], *n13 [4];
3301         bool Ok12 = false, Ok13 = false;
3302         const SMDS_MeshNode *linkNode1, *linkNode2;
3303         if(tr2) {
3304           linkNode1 = link12->first;
3305           linkNode2 = link12->second;
3306           if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
3307             Ok12 = true;
3308         }
3309         if(tr3) {
3310           linkNode1 = link13->first;
3311           linkNode2 = link13->second;
3312           if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
3313             Ok13 = true;
3314         }
3315
3316         // Choose a pair to fuse
3317         if ( Ok12 && Ok13 ) {
3318           SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
3319           SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
3320           double aBadRate12 = getBadRate( &quad12, theCrit );
3321           double aBadRate13 = getBadRate( &quad13, theCrit );
3322           if (  aBadRate13 < aBadRate12 )
3323             Ok12 = false;
3324           else
3325             Ok13 = false;
3326         }
3327
3328         // Make quadrangles
3329         // and remove fused elems and remove links from the maps
3330         mapEl_setLi.erase( tr1 );
3331         if ( Ok12 )
3332         {
3333           mapEl_setLi.erase( tr2 );
3334           mapLi_listEl.erase( *link12 );
3335           if ( tr1->NbNodes() == 3 )
3336           {
3337             const SMDS_MeshElement* newElem = 0;
3338             newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
3339             myLastCreatedElems.Append(newElem);
3340             AddToSameGroups( newElem, tr1, aMesh );
3341             int aShapeId = tr1->getshapeId();
3342             if ( aShapeId )
3343               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3344             aMesh->RemoveElement( tr1 );
3345             aMesh->RemoveElement( tr2 );
3346           }
3347           else {
3348             vector< const SMDS_MeshNode* > N1;
3349             vector< const SMDS_MeshNode* > N2;
3350             getNodesFromTwoTria(tr1,tr2,N1,N2);
3351             // now we receive following N1 and N2 (using numeration as in image in InverseDiag())
3352             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
3353             // i.e. first nodes from both arrays form a new diagonal
3354             const SMDS_MeshNode* aNodes[8];
3355             aNodes[0] = N1[0];
3356             aNodes[1] = N1[1];
3357             aNodes[2] = N2[0];
3358             aNodes[3] = N2[1];
3359             aNodes[4] = N1[3];
3360             aNodes[5] = N2[5];
3361             aNodes[6] = N2[3];
3362             aNodes[7] = N1[5];
3363             const SMDS_MeshElement* newElem = 0;
3364             if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3365               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3366                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3367             else
3368               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3369                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3370             myLastCreatedElems.Append(newElem);
3371             AddToSameGroups( newElem, tr1, aMesh );
3372             int aShapeId = tr1->getshapeId();
3373             if ( aShapeId )
3374               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3375             aMesh->RemoveElement( tr1 );
3376             aMesh->RemoveElement( tr2 );
3377             // remove middle node (9)
3378             if ( N1[4]->NbInverseElements() == 0 )
3379               aMesh->RemoveNode( N1[4] );
3380             if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3381               aMesh->RemoveNode( N1[6] );
3382             if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3383               aMesh->RemoveNode( N2[6] );
3384           }
3385         }
3386         else if ( Ok13 )
3387         {
3388           mapEl_setLi.erase( tr3 );
3389           mapLi_listEl.erase( *link13 );
3390           if ( tr1->NbNodes() == 3 ) {
3391             const SMDS_MeshElement* newElem = 0;
3392             newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
3393             myLastCreatedElems.Append(newElem);
3394             AddToSameGroups( newElem, tr1, aMesh );
3395             int aShapeId = tr1->getshapeId();
3396             if ( aShapeId )
3397               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3398             aMesh->RemoveElement( tr1 );
3399             aMesh->RemoveElement( tr3 );
3400           }
3401           else {
3402             vector< const SMDS_MeshNode* > N1;
3403             vector< const SMDS_MeshNode* > N2;
3404             getNodesFromTwoTria(tr1,tr3,N1,N2);
3405             // now we receive following N1 and N2 (using numeration as above image)
3406             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
3407             // i.e. first nodes from both arrays form a new diagonal
3408             const SMDS_MeshNode* aNodes[8];
3409             aNodes[0] = N1[0];
3410             aNodes[1] = N1[1];
3411             aNodes[2] = N2[0];
3412             aNodes[3] = N2[1];
3413             aNodes[4] = N1[3];
3414             aNodes[5] = N2[5];
3415             aNodes[6] = N2[3];
3416             aNodes[7] = N1[5];
3417             const SMDS_MeshElement* newElem = 0;
3418             if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3419               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3420                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3421             else
3422               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3423                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3424             myLastCreatedElems.Append(newElem);
3425             AddToSameGroups( newElem, tr1, aMesh );
3426             int aShapeId = tr1->getshapeId();
3427             if ( aShapeId )
3428               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3429             aMesh->RemoveElement( tr1 );
3430             aMesh->RemoveElement( tr3 );
3431             // remove middle node (9)
3432             if ( N1[4]->NbInverseElements() == 0 )
3433               aMesh->RemoveNode( N1[4] );
3434             if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3435               aMesh->RemoveNode( N1[6] );
3436             if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3437               aMesh->RemoveNode( N2[6] );
3438           }
3439         }
3440
3441         // Next element to fuse: the rejected one
3442         if ( tr3 )
3443           startElem = Ok12 ? tr3 : tr2;
3444
3445       } // if ( startElem )
3446     } // while ( startElem || !startLinks.empty() )
3447   } // while ( ! mapEl_setLi.empty() )
3448
3449   return true;
3450 }
3451
3452
3453 /*#define DUMPSO(txt) \
3454 //  cout << txt << endl;
3455 //=============================================================================
3456 //
3457 //
3458 //
3459 //=============================================================================
3460 static void swap( int i1, int i2, int idNodes[], gp_Pnt P[] )
3461 {
3462 if ( i1 == i2 )
3463 return;
3464 int tmp = idNodes[ i1 ];
3465 idNodes[ i1 ] = idNodes[ i2 ];
3466 idNodes[ i2 ] = tmp;
3467 gp_Pnt Ptmp = P[ i1 ];
3468 P[ i1 ] = P[ i2 ];
3469 P[ i2 ] = Ptmp;
3470 DUMPSO( i1 << "(" << idNodes[ i2 ] << ") <-> " << i2 << "(" << idNodes[ i1 ] << ")");
3471 }
3472
3473 //=======================================================================
3474 //function : SortQuadNodes
3475 //purpose  : Set 4 nodes of a quadrangle face in a good order.
3476 //           Swap 1<->2 or 2<->3 nodes and correspondingly return
3477 //           1 or 2 else 0.
3478 //=======================================================================
3479
3480 int SMESH_MeshEditor::SortQuadNodes (const SMDS_Mesh * theMesh,
3481 int               idNodes[] )
3482 {
3483   gp_Pnt P[4];
3484   int i;
3485   for ( i = 0; i < 4; i++ ) {
3486     const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
3487     if ( !n ) return 0;
3488     P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
3489   }
3490
3491   gp_Vec V1(P[0], P[1]);
3492   gp_Vec V2(P[0], P[2]);
3493   gp_Vec V3(P[0], P[3]);
3494
3495   gp_Vec Cross1 = V1 ^ V2;
3496   gp_Vec Cross2 = V2 ^ V3;
3497
3498   i = 0;
3499   if (Cross1.Dot(Cross2) < 0)
3500   {
3501     Cross1 = V2 ^ V1;
3502     Cross2 = V1 ^ V3;
3503
3504     if (Cross1.Dot(Cross2) < 0)
3505       i = 2;
3506     else
3507       i = 1;
3508     swap ( i, i + 1, idNodes, P );
3509
3510     //     for ( int ii = 0; ii < 4; ii++ ) {
3511     //       const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
3512     //       DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3513     //     }
3514   }
3515   return i;
3516 }
3517
3518 //=======================================================================
3519 //function : SortHexaNodes
3520 //purpose  : Set 8 nodes of a hexahedron in a good order.
3521 //           Return success status
3522 //=======================================================================
3523
3524 bool SMESH_MeshEditor::SortHexaNodes (const SMDS_Mesh * theMesh,
3525                                       int               idNodes[] )
3526 {
3527   gp_Pnt P[8];
3528   int i;
3529   DUMPSO( "INPUT: ========================================");
3530   for ( i = 0; i < 8; i++ ) {
3531     const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
3532     if ( !n ) return false;
3533     P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
3534     DUMPSO( i << "(" << idNodes[i] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3535   }
3536   DUMPSO( "========================================");
3537
3538
3539   set<int> faceNodes;  // ids of bottom face nodes, to be found
3540   set<int> checkedId1; // ids of tried 2-nd nodes
3541   Standard_Real leastDist = DBL_MAX; // dist of the 4-th node from 123 plane
3542   const Standard_Real tol = 1.e-6;   // tolerance to find nodes in plane
3543   int iMin, iLoop1 = 0;
3544
3545   // Loop to try the 2-nd nodes
3546
3547   while ( leastDist > DBL_MIN && ++iLoop1 < 8 )
3548   {
3549     // Find not checked 2-nd node
3550     for ( i = 1; i < 8; i++ )
3551       if ( checkedId1.find( idNodes[i] ) == checkedId1.end() ) {
3552         int id1 = idNodes[i];
3553         swap ( 1, i, idNodes, P );
3554         checkedId1.insert ( id1 );
3555         break;
3556       }
3557
3558     // Find the 3-d node so that 1-2-3 triangle to be on a hexa face,
3559     // ie that all but meybe one (id3 which is on the same face) nodes
3560     // lay on the same side from the triangle plane.
3561
3562     bool manyInPlane = false; // more than 4 nodes lay in plane
3563     int iLoop2 = 0;
3564     while ( ++iLoop2 < 6 ) {
3565
3566       // get 1-2-3 plane coeffs
3567       Standard_Real A, B, C, D;
3568       gp_Vec N = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
3569       if ( N.SquareMagnitude() > gp::Resolution() )
3570       {
3571         gp_Pln pln ( P[0], N );
3572         pln.Coefficients( A, B, C, D );
3573
3574         // find the node (iMin) closest to pln
3575         Standard_Real dist[ 8 ], minDist = DBL_MAX;
3576         set<int> idInPln;
3577         for ( i = 3; i < 8; i++ ) {
3578           dist[i] = A * P[i].X() + B * P[i].Y() + C * P[i].Z() + D;
3579           if ( fabs( dist[i] ) < minDist ) {
3580             minDist = fabs( dist[i] );
3581             iMin = i;
3582           }
3583           if ( fabs( dist[i] ) <= tol )
3584             idInPln.insert( idNodes[i] );
3585         }
3586
3587         // there should not be more than 4 nodes in bottom plane
3588         if ( idInPln.size() > 1 )
3589         {
3590           DUMPSO( "### idInPln.size() = " << idInPln.size());
3591           // idInPlane does not contain the first 3 nodes
3592           if ( manyInPlane || idInPln.size() == 5)
3593             return false; // all nodes in one plane
3594           manyInPlane = true;
3595
3596           // set the 1-st node to be not in plane
3597           for ( i = 3; i < 8; i++ ) {
3598             if ( idInPln.find( idNodes[ i ] ) == idInPln.end() ) {
3599               DUMPSO( "### Reset 0-th node");
3600               swap( 0, i, idNodes, P );
3601               break;
3602             }
3603           }
3604
3605           // reset to re-check second nodes
3606           leastDist = DBL_MAX;
3607           faceNodes.clear();
3608           checkedId1.clear();
3609           iLoop1 = 0;
3610           break; // from iLoop2;
3611         }
3612
3613         // check that the other 4 nodes are on the same side
3614         bool sameSide = true;
3615         bool isNeg = dist[ iMin == 3 ? 4 : 3 ] <= 0.;
3616         for ( i = 3; sameSide && i < 8; i++ ) {
3617           if ( i != iMin )
3618             sameSide = ( isNeg == dist[i] <= 0.);
3619         }
3620
3621         // keep best solution
3622         if ( sameSide && minDist < leastDist ) {
3623           leastDist = minDist;
3624           faceNodes.clear();
3625           faceNodes.insert( idNodes[ 1 ] );
3626           faceNodes.insert( idNodes[ 2 ] );
3627           faceNodes.insert( idNodes[ iMin ] );
3628           DUMPSO( "loop " << iLoop2 << " id2 " << idNodes[ 1 ] << " id3 " << idNodes[ 2 ]
3629                   << " leastDist = " << leastDist);
3630           if ( leastDist <= DBL_MIN )
3631             break;
3632         }
3633       }
3634
3635       // set next 3-d node to check
3636       int iNext = 2 + iLoop2;
3637       if ( iNext < 8 ) {
3638         DUMPSO( "Try 2-nd");
3639         swap ( 2, iNext, idNodes, P );
3640       }
3641     } // while ( iLoop2 < 6 )
3642   } // iLoop1
3643
3644   if ( faceNodes.empty() ) return false;
3645
3646   // Put the faceNodes in proper places
3647   for ( i = 4; i < 8; i++ ) {
3648     if ( faceNodes.find( idNodes[ i ] ) != faceNodes.end() ) {
3649       // find a place to put
3650       int iTo = 1;
3651       while ( faceNodes.find( idNodes[ iTo ] ) != faceNodes.end() )
3652         iTo++;
3653       DUMPSO( "Set faceNodes");
3654       swap ( iTo, i, idNodes, P );
3655     }
3656   }
3657
3658
3659   // Set nodes of the found bottom face in good order
3660   DUMPSO( " Found bottom face: ");
3661   i = SortQuadNodes( theMesh, idNodes );
3662   if ( i ) {
3663     gp_Pnt Ptmp = P[ i ];
3664     P[ i ] = P[ i+1 ];
3665     P[ i+1 ] = Ptmp;
3666   }
3667   //   else
3668   //     for ( int ii = 0; ii < 4; ii++ ) {
3669   //       const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
3670   //       DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3671   //    }
3672
3673   // Gravity center of the top and bottom faces
3674   gp_Pnt aGCb = ( P[0].XYZ() + P[1].XYZ() + P[2].XYZ() + P[3].XYZ() ) / 4.;
3675   gp_Pnt aGCt = ( P[4].XYZ() + P[5].XYZ() + P[6].XYZ() + P[7].XYZ() ) / 4.;
3676
3677   // Get direction from the bottom to the top face
3678   gp_Vec upDir ( aGCb, aGCt );
3679   Standard_Real upDirSize = upDir.Magnitude();
3680   if ( upDirSize <= gp::Resolution() ) return false;
3681   upDir / upDirSize;
3682
3683   // Assure that the bottom face normal points up
3684   gp_Vec Nb = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
3685   Nb += gp_Vec (P[0], P[2]).Crossed( gp_Vec (P[0], P[3]) );
3686   if ( Nb.Dot( upDir ) < 0 ) {
3687     DUMPSO( "Reverse bottom face");
3688     swap( 1, 3, idNodes, P );
3689   }
3690
3691   // Find 5-th node - the one closest to the 1-st among the last 4 nodes.
3692   Standard_Real minDist = DBL_MAX;
3693   for ( i = 4; i < 8; i++ ) {
3694     // projection of P[i] to the plane defined by P[0] and upDir
3695     gp_Pnt Pp = P[i].Translated( upDir * ( upDir.Dot( gp_Vec( P[i], P[0] ))));
3696     Standard_Real sqDist = P[0].SquareDistance( Pp );
3697     if ( sqDist < minDist ) {
3698       minDist = sqDist;
3699       iMin = i;
3700     }
3701   }
3702   DUMPSO( "Set 4-th");
3703   swap ( 4, iMin, idNodes, P );
3704
3705   // Set nodes of the top face in good order
3706   DUMPSO( "Sort top face");
3707   i = SortQuadNodes( theMesh, &idNodes[4] );
3708   if ( i ) {
3709     i += 4;
3710     gp_Pnt Ptmp = P[ i ];
3711     P[ i ] = P[ i+1 ];
3712     P[ i+1 ] = Ptmp;
3713   }
3714
3715   // Assure that direction of the top face normal is from the bottom face
3716   gp_Vec Nt = gp_Vec (P[4], P[5]).Crossed( gp_Vec (P[4], P[6]) );
3717   Nt += gp_Vec (P[4], P[6]).Crossed( gp_Vec (P[4], P[7]) );
3718   if ( Nt.Dot( upDir ) < 0 ) {
3719     DUMPSO( "Reverse top face");
3720     swap( 5, 7, idNodes, P );
3721   }
3722
3723   //   DUMPSO( "OUTPUT: ========================================");
3724   //   for ( i = 0; i < 8; i++ ) {
3725   //     float *p = ugrid->GetPoint(idNodes[i]);
3726   //     DUMPSO( i << "(" << idNodes[i] << ") : " << p[0] << " " << p[1] << " " << p[2]);
3727   //   }
3728
3729   return true;
3730 }*/
3731
3732 //================================================================================
3733 /*!
3734  * \brief Return nodes linked to the given one
3735  * \param theNode - the node
3736  * \param linkedNodes - the found nodes
3737  * \param type - the type of elements to check
3738  *
3739  * Medium nodes are ignored
3740  */
3741 //================================================================================
3742
3743 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
3744                                        TIDSortedElemSet &   linkedNodes,
3745                                        SMDSAbs_ElementType  type )
3746 {
3747   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
3748   while ( elemIt->more() )
3749   {
3750     const SMDS_MeshElement* elem = elemIt->next();
3751     if(elem->GetType() == SMDSAbs_0DElement)
3752       continue;
3753
3754     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
3755     if ( elem->GetType() == SMDSAbs_Volume )
3756     {
3757       SMDS_VolumeTool vol( elem );
3758       while ( nodeIt->more() ) {
3759         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3760         if ( theNode != n && vol.IsLinked( theNode, n ))
3761           linkedNodes.insert( n );
3762       }
3763     }
3764     else
3765     {
3766       for ( int i = 0; nodeIt->more(); ++i ) {
3767         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3768         if ( n == theNode ) {
3769           int iBefore = i - 1;
3770           int iAfter  = i + 1;
3771           if ( elem->IsQuadratic() ) {
3772             int nb = elem->NbNodes() / 2;
3773             iAfter  = SMESH_MesherHelper::WrapIndex( iAfter, nb );
3774             iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
3775           }
3776           linkedNodes.insert( elem->GetNodeWrap( iAfter ));
3777           linkedNodes.insert( elem->GetNodeWrap( iBefore ));
3778         }
3779       }
3780     }
3781   }
3782 }
3783
3784 //=======================================================================
3785 //function : laplacianSmooth
3786 //purpose  : pulls theNode toward the center of surrounding nodes directly
3787 //           connected to that node along an element edge
3788 //=======================================================================
3789
3790 void laplacianSmooth(const SMDS_MeshNode*                 theNode,
3791                      const Handle(Geom_Surface)&          theSurface,
3792                      map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3793 {
3794   // find surrounding nodes
3795
3796   TIDSortedElemSet nodeSet;
3797   SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
3798
3799   // compute new coodrs
3800
3801   double coord[] = { 0., 0., 0. };
3802   TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3803   for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
3804     const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3805     if ( theSurface.IsNull() ) { // smooth in 3D
3806       coord[0] += node->X();
3807       coord[1] += node->Y();
3808       coord[2] += node->Z();
3809     }
3810     else { // smooth in 2D
3811       ASSERT( theUVMap.find( node ) != theUVMap.end() );
3812       gp_XY* uv = theUVMap[ node ];
3813       coord[0] += uv->X();
3814       coord[1] += uv->Y();
3815     }
3816   }
3817   int nbNodes = nodeSet.size();
3818   if ( !nbNodes )
3819     return;
3820   coord[0] /= nbNodes;
3821   coord[1] /= nbNodes;
3822
3823   if ( !theSurface.IsNull() ) {
3824     ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3825     theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3826     gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3827     coord[0] = p3d.X();
3828     coord[1] = p3d.Y();
3829     coord[2] = p3d.Z();
3830   }
3831   else
3832     coord[2] /= nbNodes;
3833
3834   // move node
3835
3836   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3837 }
3838
3839 //=======================================================================
3840 //function : centroidalSmooth
3841 //purpose  : pulls theNode toward the element-area-weighted centroid of the
3842 //           surrounding elements
3843 //=======================================================================
3844
3845 void centroidalSmooth(const SMDS_MeshNode*                 theNode,
3846                       const Handle(Geom_Surface)&          theSurface,
3847                       map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3848 {
3849   gp_XYZ aNewXYZ(0.,0.,0.);
3850   SMESH::Controls::Area anAreaFunc;
3851   double totalArea = 0.;
3852   int nbElems = 0;
3853
3854   // compute new XYZ
3855
3856   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3857   while ( elemIt->more() )
3858   {
3859     const SMDS_MeshElement* elem = elemIt->next();
3860     nbElems++;
3861
3862     gp_XYZ elemCenter(0.,0.,0.);
3863     SMESH::Controls::TSequenceOfXYZ aNodePoints;
3864     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3865     int nn = elem->NbNodes();
3866     if(elem->IsQuadratic()) nn = nn/2;
3867     int i=0;
3868     //while ( itN->more() ) {
3869     while ( i<nn ) {
3870       const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3871       i++;
3872       gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3873       aNodePoints.push_back( aP );
3874       if ( !theSurface.IsNull() ) { // smooth in 2D
3875         ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3876         gp_XY* uv = theUVMap[ aNode ];
3877         aP.SetCoord( uv->X(), uv->Y(), 0. );
3878       }
3879       elemCenter += aP;
3880     }
3881     double elemArea = anAreaFunc.GetValue( aNodePoints );
3882     totalArea += elemArea;
3883     elemCenter /= nn;
3884     aNewXYZ += elemCenter * elemArea;
3885   }
3886   aNewXYZ /= totalArea;
3887   if ( !theSurface.IsNull() ) {
3888     theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3889     aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3890   }
3891
3892   // move node
3893
3894   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3895 }
3896
3897 //=======================================================================
3898 //function : getClosestUV
3899 //purpose  : return UV of closest projection
3900 //=======================================================================
3901
3902 static bool getClosestUV (Extrema_GenExtPS& projector,
3903                           const gp_Pnt&     point,
3904                           gp_XY &           result)
3905 {
3906   projector.Perform( point );
3907   if ( projector.IsDone() ) {
3908     double u, v, minVal = DBL_MAX;
3909     for ( int i = projector.NbExt(); i > 0; i-- )
3910       if ( projector.SquareDistance( i ) < minVal ) {
3911         minVal = projector.SquareDistance( i );
3912         projector.Point( i ).Parameter( u, v );
3913       }
3914     result.SetCoord( u, v );
3915     return true;
3916   }
3917   return false;
3918 }
3919
3920 //=======================================================================
3921 //function : Smooth
3922 //purpose  : Smooth theElements during theNbIterations or until a worst
3923 //           element has aspect ratio <= theTgtAspectRatio.
3924 //           Aspect Ratio varies in range [1.0, inf].
3925 //           If theElements is empty, the whole mesh is smoothed.
3926 //           theFixedNodes contains additionally fixed nodes. Nodes built
3927 //           on edges and boundary nodes are always fixed.
3928 //=======================================================================
3929
3930 void SMESH_MeshEditor::Smooth (TIDSortedElemSet &          theElems,
3931                                set<const SMDS_MeshNode*> & theFixedNodes,
3932                                const SmoothMethod          theSmoothMethod,
3933                                const int                   theNbIterations,
3934                                double                      theTgtAspectRatio,
3935                                const bool                  the2D)
3936 {
3937   myLastCreatedElems.Clear();
3938   myLastCreatedNodes.Clear();
3939
3940   if ( theTgtAspectRatio < 1.0 )
3941     theTgtAspectRatio = 1.0;
3942
3943   const double disttol = 1.e-16;
3944
3945   SMESH::Controls::AspectRatio aQualityFunc;
3946
3947   SMESHDS_Mesh* aMesh = GetMeshDS();
3948
3949   if ( theElems.empty() ) {
3950     // add all faces to theElems
3951     SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3952     while ( fIt->more() ) {
3953       const SMDS_MeshElement* face = fIt->next();
3954       theElems.insert( theElems.end(), face );
3955     }
3956   }
3957   // get all face ids theElems are on
3958   set< int > faceIdSet;
3959   TIDSortedElemSet::iterator itElem;
3960   if ( the2D )
3961     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3962       int fId = FindShape( *itElem );
3963       // check that corresponding submesh exists and a shape is face
3964       if (fId &&
3965           faceIdSet.find( fId ) == faceIdSet.end() &&
3966           aMesh->MeshElements( fId )) {
3967         TopoDS_Shape F = aMesh->IndexToShape( fId );
3968         if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3969           faceIdSet.insert( fId );
3970       }
3971     }
3972   faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3973
3974   // ===============================================
3975   // smooth elements on each TopoDS_Face separately
3976   // ===============================================
3977
3978   SMESH_MesherHelper helper( *GetMesh() );
3979
3980   set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treat 0 fId at the end
3981   for ( ; fId != faceIdSet.rend(); ++fId )
3982   {
3983     // get face surface and submesh
3984     Handle(Geom_Surface) surface;
3985     SMESHDS_SubMesh* faceSubMesh = 0;
3986     TopoDS_Face face;
3987     double fToler2 = 0;
3988     double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3989     bool isUPeriodic = false, isVPeriodic = false;
3990     if ( *fId )
3991     {
3992       face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3993       surface = BRep_Tool::Surface( face );
3994       faceSubMesh = aMesh->MeshElements( *fId );
3995       fToler2 = BRep_Tool::Tolerance( face );
3996       fToler2 *= fToler2 * 10.;
3997       isUPeriodic = surface->IsUPeriodic();
3998       // if ( isUPeriodic )
3999       //   surface->UPeriod();
4000       isVPeriodic = surface->IsVPeriodic();
4001       // if ( isVPeriodic )
4002       //   surface->VPeriod();
4003       surface->Bounds( u1, u2, v1, v2 );
4004       helper.SetSubShape( face );
4005     }
4006     // ---------------------------------------------------------
4007     // for elements on a face, find movable and fixed nodes and
4008     // compute UV for them
4009     // ---------------------------------------------------------
4010     bool checkBoundaryNodes = false;
4011     bool isQuadratic = false;
4012     set<const SMDS_MeshNode*> setMovableNodes;
4013     map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
4014     list< gp_XY > listUV; // uvs the 2 uvMaps refer to
4015     list< const SMDS_MeshElement* > elemsOnFace;
4016
4017     Extrema_GenExtPS projector;
4018     GeomAdaptor_Surface surfAdaptor;
4019     if ( !surface.IsNull() ) {
4020       surfAdaptor.Load( surface );
4021       projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
4022     }
4023     int nbElemOnFace = 0;
4024     itElem = theElems.begin();
4025     // loop on not yet smoothed elements: look for elems on a face
4026     while ( itElem != theElems.end() )
4027     {
4028       if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
4029         break; // all elements found
4030
4031       const SMDS_MeshElement* elem = *itElem;
4032       if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
4033            ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
4034         ++itElem;
4035         continue;
4036       }
4037       elemsOnFace.push_back( elem );
4038       theElems.erase( itElem++ );
4039       nbElemOnFace++;
4040
4041       if ( !isQuadratic )
4042         isQuadratic = elem->IsQuadratic();
4043
4044       // get movable nodes of elem
4045       const SMDS_MeshNode* node;
4046       SMDS_TypeOfPosition posType;
4047       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4048       int nn = 0, nbn =  elem->NbNodes();
4049       if(elem->IsQuadratic())
4050         nbn = nbn/2;
4051       while ( nn++ < nbn ) {
4052         node = static_cast<const SMDS_MeshNode*>( itN->next() );
4053         const SMDS_PositionPtr& pos = node->GetPosition();
4054         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
4055         if (posType != SMDS_TOP_EDGE &&
4056             posType != SMDS_TOP_VERTEX &&
4057             theFixedNodes.find( node ) == theFixedNodes.end())
4058         {
4059           // check if all faces around the node are on faceSubMesh
4060           // because a node on edge may be bound to face
4061           bool all = true;
4062           if ( faceSubMesh ) {
4063             SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
4064             while ( eIt->more() && all ) {
4065               const SMDS_MeshElement* e = eIt->next();
4066               all = faceSubMesh->Contains( e );
4067             }
4068           }
4069           if ( all )
4070             setMovableNodes.insert( node );
4071           else
4072             checkBoundaryNodes = true;
4073         }
4074         if ( posType == SMDS_TOP_3DSPACE )
4075           checkBoundaryNodes = true;
4076       }
4077
4078       if ( surface.IsNull() )
4079         continue;
4080
4081       // get nodes to check UV
4082       list< const SMDS_MeshNode* > uvCheckNodes;
4083       const SMDS_MeshNode* nodeInFace = 0;
4084       itN = elem->nodesIterator();
4085       nn = 0; nbn =  elem->NbNodes();
4086       if(elem->IsQuadratic())
4087         nbn = nbn/2;
4088       while ( nn++ < nbn ) {
4089         node = static_cast<const SMDS_MeshNode*>( itN->next() );
4090         if ( node->GetPosition()->GetDim() == 2 )
4091           nodeInFace = node;
4092         if ( uvMap.find( node ) == uvMap.end() )
4093           uvCheckNodes.push_back( node );
4094         // add nodes of elems sharing node
4095         //         SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
4096         //         while ( eIt->more() ) {
4097         //           const SMDS_MeshElement* e = eIt->next();
4098         //           if ( e != elem ) {
4099         //             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4100         //             while ( nIt->more() ) {
4101         //               const SMDS_MeshNode* n =
4102         //                 static_cast<const SMDS_MeshNode*>( nIt->next() );
4103         //               if ( uvMap.find( n ) == uvMap.end() )
4104         //                 uvCheckNodes.push_back( n );
4105         //             }
4106         //           }
4107         //         }
4108       }
4109       // check UV on face
4110       list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
4111       for ( ; n != uvCheckNodes.end(); ++n ) {
4112         node = *n;
4113         gp_XY uv( 0, 0 );
4114         const SMDS_PositionPtr& pos = node->GetPosition();
4115         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
4116         // get existing UV
4117         if ( pos )
4118         {
4119           bool toCheck = true;
4120           uv = helper.GetNodeUV( face, node, nodeInFace, &toCheck );
4121         }
4122         // compute not existing UV
4123         bool project = ( posType == SMDS_TOP_3DSPACE );
4124         // double dist1 = DBL_MAX, dist2 = 0;
4125         // if ( posType != SMDS_TOP_3DSPACE ) {
4126         //   dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
4127         //   project = dist1 > fToler2;
4128         // }
4129         if ( project ) { // compute new UV
4130           gp_XY newUV;
4131           gp_Pnt pNode = SMESH_TNodeXYZ( node );
4132           if ( !getClosestUV( projector, pNode, newUV )) {
4133             MESSAGE("Node Projection Failed " << node);
4134           }
4135           else {
4136             if ( isUPeriodic )
4137               newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
4138             if ( isVPeriodic )
4139               newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
4140             // check new UV
4141             // if ( posType != SMDS_TOP_3DSPACE )
4142             //   dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
4143             // if ( dist2 < dist1 )
4144               uv = newUV;
4145           }
4146         }
4147         // store UV in the map
4148         listUV.push_back( uv );
4149         uvMap.insert( make_pair( node, &listUV.back() ));
4150       }
4151     } // loop on not yet smoothed elements
4152
4153     if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
4154       checkBoundaryNodes = true;
4155
4156     // fix nodes on mesh boundary
4157
4158     if ( checkBoundaryNodes ) {
4159       map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
4160       map< SMESH_TLink, int >::iterator link_nb;
4161       // put all elements links to linkNbMap
4162       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4163       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4164         const SMDS_MeshElement* elem = (*elemIt);
4165         int nbn =  elem->NbCornerNodes();
4166         // loop on elem links: insert them in linkNbMap
4167         for ( int iN = 0; iN < nbn; ++iN ) {
4168           const SMDS_MeshNode* n1 = elem->GetNode( iN );
4169           const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
4170           SMESH_TLink link( n1, n2 );
4171           link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
4172           link_nb->second++;
4173         }
4174       }
4175       // remove nodes that are in links encountered only once from setMovableNodes
4176       for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
4177         if ( link_nb->second == 1 ) {
4178           setMovableNodes.erase( link_nb->first.node1() );
4179           setMovableNodes.erase( link_nb->first.node2() );
4180         }
4181       }
4182     }
4183
4184     // -----------------------------------------------------
4185     // for nodes on seam edge, compute one more UV ( uvMap2 );
4186     // find movable nodes linked to nodes on seam and which
4187     // are to be smoothed using the second UV ( uvMap2 )
4188     // -----------------------------------------------------
4189
4190     set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
4191     if ( !surface.IsNull() ) {
4192       TopExp_Explorer eExp( face, TopAbs_EDGE );
4193       for ( ; eExp.More(); eExp.Next() ) {
4194         TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
4195         if ( !BRep_Tool::IsClosed( edge, face ))
4196           continue;
4197         SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
4198         if ( !sm ) continue;
4199         // find out which parameter varies for a node on seam
4200         double f,l;
4201         gp_Pnt2d uv1, uv2;
4202         Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
4203         if ( pcurve.IsNull() ) continue;
4204         uv1 = pcurve->Value( f );
4205         edge.Reverse();
4206         pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
4207         if ( pcurve.IsNull() ) continue;
4208         uv2 = pcurve->Value( f );
4209         int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
4210         // assure uv1 < uv2
4211         if ( uv1.Coord( iPar ) > uv2.Coord( iPar ))
4212           std::swap( uv1, uv2 );
4213         // get nodes on seam and its vertices
4214         list< const SMDS_MeshNode* > seamNodes;
4215         SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
4216         while ( nSeamIt->more() ) {
4217           const SMDS_MeshNode* node = nSeamIt->next();
4218           if ( !isQuadratic || !IsMedium( node ))
4219             seamNodes.push_back( node );
4220         }
4221         TopExp_Explorer vExp( edge, TopAbs_VERTEX );
4222         for ( ; vExp.More(); vExp.Next() ) {
4223           sm = aMesh->MeshElements( vExp.Current() );
4224           if ( sm ) {
4225             nSeamIt = sm->GetNodes();
4226             while ( nSeamIt->more() )
4227               seamNodes.push_back( nSeamIt->next() );
4228           }
4229         }
4230         // loop on nodes on seam
4231         list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
4232         for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
4233           const SMDS_MeshNode* nSeam = *noSeIt;
4234           map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
4235           if ( n_uv == uvMap.end() )
4236             continue;
4237           // set the first UV
4238           n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
4239           // set the second UV
4240           listUV.push_back( *n_uv->second );
4241           listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
4242           if ( uvMap2.empty() )
4243             uvMap2 = uvMap; // copy the uvMap contents
4244           uvMap2[ nSeam ] = &listUV.back();
4245
4246           // collect movable nodes linked to ones on seam in nodesNearSeam
4247           SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
4248           while ( eIt->more() ) {
4249             const SMDS_MeshElement* e = eIt->next();
4250             int nbUseMap1 = 0, nbUseMap2 = 0;
4251             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4252             int nn = 0, nbn =  e->NbNodes();
4253             if(e->IsQuadratic()) nbn = nbn/2;
4254             while ( nn++ < nbn )
4255             {
4256               const SMDS_MeshNode* n =
4257                 static_cast<const SMDS_MeshNode*>( nIt->next() );
4258               if (n == nSeam ||
4259                   setMovableNodes.find( n ) == setMovableNodes.end() )
4260                 continue;
4261               // add only nodes being closer to uv2 than to uv1
4262               // gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
4263               //              0.5 * ( n->Y() + nSeam->Y() ),
4264               //              0.5 * ( n->Z() + nSeam->Z() ));
4265               // gp_XY uv;
4266               // getClosestUV( projector, pMid, uv );
4267               double x = uvMap[ n ]->Coord( iPar );
4268               if ( Abs( uv1.Coord( iPar ) - x ) >
4269                    Abs( uv2.Coord( iPar ) - x )) {
4270                 nodesNearSeam.insert( n );
4271                 nbUseMap2++;
4272               }
4273               else
4274                 nbUseMap1++;
4275             }
4276             // for centroidalSmooth all element nodes must
4277             // be on one side of a seam
4278             if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
4279               SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4280               nn = 0;
4281               while ( nn++ < nbn ) {
4282                 const SMDS_MeshNode* n =
4283                   static_cast<const SMDS_MeshNode*>( nIt->next() );
4284                 setMovableNodes.erase( n );
4285               }
4286             }
4287           }
4288         } // loop on nodes on seam
4289       } // loop on edge of a face
4290     } // if ( !face.IsNull() )
4291
4292     if ( setMovableNodes.empty() ) {
4293       MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
4294       continue; // goto next face
4295     }
4296
4297     // -------------
4298     // SMOOTHING //
4299     // -------------
4300
4301     int it = -1;
4302     double maxRatio = -1., maxDisplacement = -1.;
4303     set<const SMDS_MeshNode*>::iterator nodeToMove;
4304     for ( it = 0; it < theNbIterations; it++ ) {
4305       maxDisplacement = 0.;
4306       nodeToMove = setMovableNodes.begin();
4307       for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4308         const SMDS_MeshNode* node = (*nodeToMove);
4309         gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
4310
4311         // smooth
4312         bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
4313         if ( theSmoothMethod == LAPLACIAN )
4314           laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
4315         else
4316           centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
4317
4318         // node displacement
4319         gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
4320         Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
4321         if ( aDispl > maxDisplacement )
4322           maxDisplacement = aDispl;
4323       }
4324       // no node movement => exit
4325       //if ( maxDisplacement < 1.e-16 ) {
4326       if ( maxDisplacement < disttol ) {
4327         MESSAGE("-- no node movement --");
4328         break;
4329       }
4330
4331       // check elements quality
4332       maxRatio  = 0;
4333       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4334       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4335         const SMDS_MeshElement* elem = (*elemIt);
4336         if ( !elem || elem->GetType() != SMDSAbs_Face )
4337           continue;
4338         SMESH::Controls::TSequenceOfXYZ aPoints;
4339         if ( aQualityFunc.GetPoints( elem, aPoints )) {
4340           double aValue = aQualityFunc.GetValue( aPoints );
4341           if ( aValue > maxRatio )
4342             maxRatio = aValue;
4343         }
4344       }
4345       if ( maxRatio <= theTgtAspectRatio ) {
4346         //MESSAGE("-- quality achieved --");
4347         break;
4348       }
4349       if (it+1 == theNbIterations) {
4350         //MESSAGE("-- Iteration limit exceeded --");
4351       }
4352     } // smoothing iterations
4353
4354     // MESSAGE(" Face id: " << *fId <<
4355     //         " Nb iterstions: " << it <<
4356     //         " Displacement: " << maxDisplacement <<
4357     //         " Aspect Ratio " << maxRatio);
4358
4359     // ---------------------------------------
4360     // new nodes positions are computed,
4361     // record movement in DS and set new UV
4362     // ---------------------------------------
4363     nodeToMove = setMovableNodes.begin();
4364     for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4365       SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
4366       aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
4367       map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
4368       if ( node_uv != uvMap.end() ) {
4369         gp_XY* uv = node_uv->second;
4370         node->SetPosition
4371           ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
4372       }
4373     }
4374
4375     // move medium nodes of quadratic elements
4376     if ( isQuadratic )
4377     {
4378       vector<const SMDS_MeshNode*> nodes;
4379       bool checkUV;
4380       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4381       for ( ; elemIt != elemsOnFace.end(); ++elemIt )
4382       {
4383         const SMDS_MeshElement* QF = *elemIt;
4384         if ( QF->IsQuadratic() )
4385         {
4386           nodes.assign( SMDS_MeshElement::iterator( QF->interlacedNodesElemIterator() ),
4387                         SMDS_MeshElement::iterator() );
4388           nodes.push_back( nodes[0] );
4389           gp_Pnt xyz;
4390           for (size_t i = 1; i < nodes.size(); i += 2 ) // i points to a medium node
4391           {
4392             if ( !surface.IsNull() )
4393             {
4394               gp_XY uv1 = helper.GetNodeUV( face, nodes[i-1], nodes[i+1], &checkUV );
4395               gp_XY uv2 = helper.GetNodeUV( face, nodes[i+1], nodes[i-1], &checkUV );
4396               gp_XY uv  = helper.GetMiddleUV( surface, uv1, uv2 );
4397               xyz = surface->Value( uv.X(), uv.Y() );
4398             }
4399             else {
4400               xyz = 0.5 * ( SMESH_TNodeXYZ( nodes[i-1] ) + SMESH_TNodeXYZ( nodes[i+1] ));
4401             }
4402             if (( SMESH_TNodeXYZ( nodes[i] ) - xyz.XYZ() ).Modulus() > disttol )
4403               // we have to move a medium node
4404               aMesh->MoveNode( nodes[i], xyz.X(), xyz.Y(), xyz.Z() );
4405           }
4406         }
4407       }
4408     }
4409
4410   } // loop on face ids
4411
4412 }
4413
4414 namespace
4415 {
4416   //=======================================================================
4417   //function : isReverse
4418   //purpose  : Return true if normal of prevNodes is not co-directied with
4419   //           gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
4420   //           iNotSame is where prevNodes and nextNodes are different.
4421   //           If result is true then future volume orientation is OK
4422   //=======================================================================
4423
4424   bool isReverse(const SMDS_MeshElement*             face,
4425                  const vector<const SMDS_MeshNode*>& prevNodes,
4426                  const vector<const SMDS_MeshNode*>& nextNodes,
4427                  const int                           iNotSame)
4428   {
4429
4430     SMESH_TNodeXYZ pP = prevNodes[ iNotSame ];
4431     SMESH_TNodeXYZ pN = nextNodes[ iNotSame ];
4432     gp_XYZ extrDir( pN - pP ), faceNorm;
4433     SMESH_MeshAlgos::FaceNormal( face, faceNorm, /*normalized=*/false );
4434
4435     return faceNorm * extrDir < 0.0;
4436   }
4437
4438   //================================================================================
4439   /*!
4440    * \brief Assure that theElemSets[0] holds elements, not nodes
4441    */
4442   //================================================================================
4443
4444   void setElemsFirst( TIDSortedElemSet theElemSets[2] )
4445   {
4446     if ( !theElemSets[0].empty() &&
4447          (*theElemSets[0].begin())->GetType() == SMDSAbs_Node )
4448     {
4449       std::swap( theElemSets[0], theElemSets[1] );
4450     }
4451     else if ( !theElemSets[1].empty() &&
4452               (*theElemSets[1].begin())->GetType() != SMDSAbs_Node )
4453     {
4454       std::swap( theElemSets[0], theElemSets[1] );
4455     }
4456   }
4457 }
4458
4459 //=======================================================================
4460 /*!
4461  * \brief Create elements by sweeping an element
4462  * \param elem - element to sweep
4463  * \param newNodesItVec - nodes generated from each node of the element
4464  * \param newElems - generated elements
4465  * \param nbSteps - number of sweeping steps
4466  * \param srcElements - to append elem for each generated element
4467  */
4468 //=======================================================================
4469
4470 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement*               elem,
4471                                     const vector<TNodeOfNodeListMapItr> & newNodesItVec,
4472                                     list<const SMDS_MeshElement*>&        newElems,
4473                                     const size_t                          nbSteps,
4474                                     SMESH_SequenceOfElemPtr&              srcElements)
4475 {
4476   SMESHDS_Mesh* aMesh = GetMeshDS();
4477
4478   const int           nbNodes = elem->NbNodes();
4479   const int         nbCorners = elem->NbCornerNodes();
4480   SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
4481                                                           polyhedron creation !!! */
4482   // Loop on elem nodes:
4483   // find new nodes and detect same nodes indices
4484   vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
4485   vector<const SMDS_MeshNode*> prevNod( nbNodes );
4486   vector<const SMDS_MeshNode*> nextNod( nbNodes );
4487   vector<const SMDS_MeshNode*> midlNod( nbNodes );
4488
4489   int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
4490   vector<int> sames(nbNodes);
4491   vector<bool> isSingleNode(nbNodes);
4492
4493   for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4494     TNodeOfNodeListMapItr                        nnIt = newNodesItVec[ iNode ];
4495     const SMDS_MeshNode*                         node = nnIt->first;
4496     const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
4497     if ( listNewNodes.empty() )
4498       return;
4499
4500     itNN   [ iNode ] = listNewNodes.begin();
4501     prevNod[ iNode ] = node;
4502     nextNod[ iNode ] = listNewNodes.front();
4503
4504     isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
4505                                                              corner node of linear */
4506     if ( prevNod[ iNode ] != nextNod [ iNode ])
4507       nbDouble += !isSingleNode[iNode];
4508
4509     if( iNode < nbCorners ) { // check corners only
4510       if ( prevNod[ iNode ] == nextNod [ iNode ])
4511         sames[nbSame++] = iNode;
4512       else
4513         iNotSameNode = iNode;
4514     }
4515   }
4516
4517   if ( nbSame == nbNodes || nbSame > 2) {
4518     MESSAGE( " Too many same nodes of element " << elem->GetID() );
4519     return;
4520   }
4521
4522   if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
4523   {
4524     // fix nodes order to have bottom normal external
4525     if ( baseType == SMDSEntity_Polygon )
4526     {
4527       std::reverse( itNN.begin(), itNN.end() );
4528       std::reverse( prevNod.begin(), prevNod.end() );
4529       std::reverse( midlNod.begin(), midlNod.end() );
4530       std::reverse( nextNod.begin(), nextNod.end() );
4531       std::reverse( isSingleNode.begin(), isSingleNode.end() );
4532     }
4533     else
4534     {
4535       const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType, nbNodes );
4536       SMDS_MeshCell::applyInterlace( ind, itNN );
4537       SMDS_MeshCell::applyInterlace( ind, prevNod );
4538       SMDS_MeshCell::applyInterlace( ind, nextNod );
4539       SMDS_MeshCell::applyInterlace( ind, midlNod );
4540       SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4541       if ( nbSame > 0 )
4542       {
4543         sames[nbSame] = iNotSameNode;
4544         for ( int j = 0; j <= nbSame; ++j )
4545           for ( size_t i = 0; i < ind.size(); ++i )
4546             if ( ind[i] == sames[j] )
4547             {
4548               sames[j] = i;
4549               break;
4550             }
4551         iNotSameNode = sames[nbSame];
4552       }
4553     }
4554   }
4555   else if ( elem->GetType() == SMDSAbs_Edge )
4556   {
4557     // orient a new face same as adjacent one
4558     int i1, i2;
4559     const SMDS_MeshElement* e;
4560     TIDSortedElemSet dummy;
4561     if (( e = SMESH_MeshAlgos::FindFaceInSet( nextNod[0], prevNod[0], dummy,dummy, &i1, &i2 )) ||
4562         ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[1], nextNod[1], dummy,dummy, &i1, &i2 )) ||
4563         ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[0], prevNod[1], dummy,dummy, &i1, &i2 )))
4564     {
4565       // there is an adjacent face, check order of nodes in it
4566       bool sameOrder = ( Abs( i2 - i1 ) == 1 ) ? ( i2 > i1 ) : ( i2 < i1 );
4567       if ( sameOrder )
4568       {
4569         std::swap( itNN[0],    itNN[1] );
4570         std::swap( prevNod[0], prevNod[1] );
4571         std::swap( nextNod[0], nextNod[1] );
4572 #if defined(__APPLE__)
4573         std::swap( isSingleNode[0], isSingleNode[1] );
4574 #else
4575         isSingleNode.swap( isSingleNode[0], isSingleNode[1] );
4576 #endif
4577         if ( nbSame > 0 )
4578           sames[0] = 1 - sames[0];
4579         iNotSameNode = 1 - iNotSameNode;
4580       }
4581     }
4582   }
4583
4584   int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
4585   if ( nbSame > 0 ) {
4586     iSameNode    = sames[ nbSame-1 ];
4587     iBeforeSame  = ( iSameNode + nbCorners - 1 ) % nbCorners;
4588     iAfterSame   = ( iSameNode + 1 ) % nbCorners;
4589     iOpposSame   = ( iSameNode - 2 < 0  ? iSameNode + 2 : iSameNode - 2 );
4590   }
4591
4592   if ( baseType == SMDSEntity_Polygon )
4593   {
4594     if      ( nbNodes == 3 ) baseType = SMDSEntity_Triangle;
4595     else if ( nbNodes == 4 ) baseType = SMDSEntity_Quadrangle;
4596   }
4597   else if ( baseType == SMDSEntity_Quad_Polygon )
4598   {
4599     if      ( nbNodes == 6 ) baseType = SMDSEntity_Quad_Triangle;
4600     else if ( nbNodes == 8 ) baseType = SMDSEntity_Quad_Quadrangle;
4601   }
4602
4603   // make new elements
4604   for ( size_t iStep = 0; iStep < nbSteps; iStep++ )
4605   {
4606     // get next nodes
4607     for ( iNode = 0; iNode < nbNodes; iNode++ )
4608     {
4609       midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
4610       nextNod[ iNode ] = *itNN[ iNode ]++;
4611     }
4612
4613     SMDS_MeshElement* aNewElem = 0;
4614     /*if(!elem->IsPoly())*/ {
4615       switch ( baseType ) {
4616       case SMDSEntity_0D:
4617       case SMDSEntity_Node: { // sweep NODE
4618         if ( nbSame == 0 ) {
4619           if ( isSingleNode[0] )
4620             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
4621           else
4622             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
4623         }
4624         else
4625           return;
4626         break;
4627       }
4628       case SMDSEntity_Edge: { // sweep EDGE
4629         if ( nbDouble == 0 )
4630         {
4631           if ( nbSame == 0 ) // ---> quadrangle
4632             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4633                                       nextNod[ 1 ], nextNod[ 0 ] );
4634           else               // ---> triangle
4635             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4636                                       nextNod[ iNotSameNode ] );
4637         }
4638         else                 // ---> polygon
4639         {
4640           vector<const SMDS_MeshNode*> poly_nodes;
4641           poly_nodes.push_back( prevNod[0] );
4642           poly_nodes.push_back( prevNod[1] );
4643           if ( prevNod[1] != nextNod[1] )
4644           {
4645             if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
4646             poly_nodes.push_back( nextNod[1] );
4647           }
4648           if ( prevNod[0] != nextNod[0] )
4649           {
4650             poly_nodes.push_back( nextNod[0] );
4651             if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
4652           }
4653           switch ( poly_nodes.size() ) {
4654           case 3:
4655             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
4656             break;
4657           case 4:
4658             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
4659                                        poly_nodes[ 2 ], poly_nodes[ 3 ]);
4660             break;
4661           default:
4662             aNewElem = aMesh->AddPolygonalFace (poly_nodes);
4663           }
4664         }
4665         break;
4666       }
4667       case SMDSEntity_Triangle: // TRIANGLE --->
4668         {
4669           if ( nbDouble > 0 ) break;
4670           if ( nbSame == 0 )       // ---> pentahedron
4671             aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4672                                          nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
4673
4674           else if ( nbSame == 1 )  // ---> pyramid
4675             aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4676                                          nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
4677                                          nextNod[ iSameNode ]);
4678
4679           else // 2 same nodes:       ---> tetrahedron
4680             aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4681                                          nextNod[ iNotSameNode ]);
4682           break;
4683         }
4684       case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
4685         {
4686           if ( nbSame == 2 )
4687             return;
4688           if ( nbDouble+nbSame == 2 )
4689           {
4690             if(nbSame==0) {      // ---> quadratic quadrangle
4691               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4692                                         prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
4693             }
4694             else { //(nbSame==1) // ---> quadratic triangle
4695               if(sames[0]==2) {
4696                 return; // medium node on axis
4697               }
4698               else if(sames[0]==0)
4699                 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1],
4700                                           prevNod[2], midlNod[1], nextNod[2] );
4701               else // sames[0]==1
4702                 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[0],
4703                                           prevNod[2], nextNod[2], midlNod[0]);
4704             }
4705           }
4706           else if ( nbDouble == 3 )
4707           {
4708             if ( nbSame == 0 ) {  // ---> bi-quadratic quadrangle
4709               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4710                                         prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
4711             }
4712           }
4713           else
4714             return;
4715           break;
4716         }
4717       case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
4718         if ( nbDouble > 0 ) break;
4719
4720         if ( nbSame == 0 )       // ---> hexahedron
4721           aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
4722                                        nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
4723
4724         else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
4725           aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4726                                        nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
4727                                        nextNod[ iSameNode ]);
4728           newElems.push_back( aNewElem );
4729           aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ],  prevNod[ iOpposSame ],
4730                                        prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
4731                                        nextNod[ iOpposSame ],  nextNod[ iBeforeSame ] );
4732         }
4733         else if ( nbSame == 2 ) { // ---> pentahedron
4734           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
4735             // iBeforeSame is same too
4736             aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
4737                                          nextNod[ iOpposSame ],  prevNod[ iSameNode ],
4738                                          prevNod[ iAfterSame ],  nextNod[ iAfterSame ]);
4739           else
4740             // iAfterSame is same too
4741             aNewElem = aMesh->AddVolume (prevNod[ iSameNode ],   prevNod[ iBeforeSame ],
4742                                          nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
4743                                          prevNod[ iOpposSame ],  nextNod[ iOpposSame ]);
4744         }
4745         break;
4746       }
4747       case SMDSEntity_Quad_Triangle:  // sweep (Bi)Quadratic TRIANGLE --->
4748       case SMDSEntity_BiQuad_Triangle: /* ??? */ {
4749         if ( nbDouble+nbSame != 3 ) break;
4750         if(nbSame==0) {
4751           // --->  pentahedron with 15 nodes
4752           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4753                                        nextNod[0], nextNod[1], nextNod[2],
4754                                        prevNod[3], prevNod[4], prevNod[5],
4755                                        nextNod[3], nextNod[4], nextNod[5],
4756                                        midlNod[0], midlNod[1], midlNod[2]);
4757         }
4758         else if(nbSame==1) {
4759           // --->  2d order pyramid of 13 nodes
4760           int apex = iSameNode;
4761           int i0 = ( apex + 1 ) % nbCorners;
4762           int i1 = ( apex - 1 + nbCorners ) % nbCorners;
4763           int i0a = apex + 3;
4764           int i1a = i1 + 3;
4765           int i01 = i0 + 3;
4766           aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
4767                                       nextNod[i0], nextNod[i1], prevNod[apex],
4768                                       prevNod[i01], midlNod[i0],
4769                                       nextNod[i01], midlNod[i1],
4770                                       prevNod[i1a], prevNod[i0a],
4771                                       nextNod[i0a], nextNod[i1a]);
4772         }
4773         else if(nbSame==2) {
4774           // --->  2d order tetrahedron of 10 nodes
4775           int n1 = iNotSameNode;
4776           int n2 = ( n1 + 1             ) % nbCorners;
4777           int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
4778           int n12 = n1 + 3;
4779           int n23 = n2 + 3;
4780           int n31 = n3 + 3;
4781           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
4782                                        prevNod[n12], prevNod[n23], prevNod[n31],
4783                                        midlNod[n1], nextNod[n12], nextNod[n31]);
4784         }
4785         break;
4786       }
4787       case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
4788         if( nbSame == 0 ) {
4789           if ( nbDouble != 4 ) break;
4790           // --->  hexahedron with 20 nodes
4791           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4792                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4793                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4794                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4795                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
4796         }
4797         else if(nbSame==1) {
4798           // ---> pyramid + pentahedron - can not be created since it is needed
4799           // additional middle node at the center of face
4800           //INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
4801           return;
4802         }
4803         else if( nbSame == 2 ) {
4804           if ( nbDouble != 2 ) break;
4805           // --->  2d order Pentahedron with 15 nodes
4806           int n1,n2,n4,n5;
4807           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
4808             // iBeforeSame is same too
4809             n1 = iBeforeSame;
4810             n2 = iOpposSame;
4811             n4 = iSameNode;
4812             n5 = iAfterSame;
4813           }
4814           else {
4815             // iAfterSame is same too
4816             n1 = iSameNode;
4817             n2 = iBeforeSame;
4818             n4 = iAfterSame;
4819             n5 = iOpposSame;
4820           }
4821           int n12 = n2 + 4;
4822           int n45 = n4 + 4;
4823           int n14 = n1 + 4;
4824           int n25 = n5 + 4;
4825           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
4826                                        prevNod[n4], prevNod[n5], nextNod[n5],
4827                                        prevNod[n12], midlNod[n2], nextNod[n12],
4828                                        prevNod[n45], midlNod[n5], nextNod[n45],
4829                                        prevNod[n14], prevNod[n25], nextNod[n25]);
4830         }
4831         break;
4832       }
4833       case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
4834
4835         if( nbSame == 0 && nbDouble == 9 ) {
4836           // --->  tri-quadratic hexahedron with 27 nodes
4837           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4838                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4839                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4840                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4841                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4842                                        prevNod[8], // bottom center
4843                                        midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4844                                        nextNod[8], // top center
4845                                        midlNod[8]);// elem center
4846         }
4847         else
4848         {
4849           return;
4850         }
4851         break;
4852       }
4853       case SMDSEntity_Polygon: { // sweep POLYGON
4854
4855         if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4856           // --->  hexagonal prism
4857           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4858                                        prevNod[3], prevNod[4], prevNod[5],
4859                                        nextNod[0], nextNod[1], nextNod[2],
4860                                        nextNod[3], nextNod[4], nextNod[5]);
4861         }
4862         break;
4863       }
4864       case SMDSEntity_Ball:
4865         return;
4866
4867       default:
4868         break;
4869       } // switch ( baseType )
4870     } // scope
4871
4872     if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4873     {
4874       if ( baseType != SMDSEntity_Polygon )
4875       {
4876         const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType,nbNodes);
4877         SMDS_MeshCell::applyInterlace( ind, prevNod );
4878         SMDS_MeshCell::applyInterlace( ind, nextNod );
4879         SMDS_MeshCell::applyInterlace( ind, midlNod );
4880         SMDS_MeshCell::applyInterlace( ind, itNN );
4881         SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4882         baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4883       }
4884       vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4885       vector<int> quantities (nbNodes + 2);
4886       polyedre_nodes.clear();
4887       quantities.clear();
4888
4889       // bottom of prism
4890       for (int inode = 0; inode < nbNodes; inode++)
4891         polyedre_nodes.push_back( prevNod[inode] );
4892       quantities.push_back( nbNodes );
4893
4894       // top of prism
4895       polyedre_nodes.push_back( nextNod[0] );
4896       for (int inode = nbNodes; inode-1; --inode )
4897         polyedre_nodes.push_back( nextNod[inode-1] );
4898       quantities.push_back( nbNodes );
4899
4900       // side faces
4901       // 3--6--2
4902       // |     |
4903       // 7     5
4904       // |     |
4905       // 0--4--1
4906       const int iQuad = elem->IsQuadratic();
4907       for (int iface = 0; iface < nbNodes; iface += 1+iQuad )
4908       {
4909         const int prevNbNodes = polyedre_nodes.size(); // to detect degenerated face
4910         int inextface = (iface+1+iQuad) % nbNodes;
4911         int imid      = (iface+1) % nbNodes;
4912         polyedre_nodes.push_back( prevNod[inextface] );         // 0
4913         if ( iQuad ) polyedre_nodes.push_back( prevNod[imid] ); // 4
4914         polyedre_nodes.push_back( prevNod[iface] );             // 1
4915         if ( prevNod[iface] != nextNod[iface] ) // 1 != 2
4916         {
4917           if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]); // 5
4918           polyedre_nodes.push_back( nextNod[iface] );                         // 2
4919         }
4920         if ( iQuad ) polyedre_nodes.push_back( nextNod[imid] );               // 6
4921         if ( prevNod[inextface] != nextNod[inextface] ) // 0 != 3
4922         {
4923           polyedre_nodes.push_back( nextNod[inextface] );                            // 3
4924           if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);// 7
4925         }
4926         const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4927         if ( nbFaceNodes > 2 )
4928           quantities.push_back( nbFaceNodes );
4929         else // degenerated face
4930           polyedre_nodes.resize( prevNbNodes );
4931       }
4932       aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4933
4934     } // try to create a polyherdal prism
4935
4936     if ( aNewElem ) {
4937       newElems.push_back( aNewElem );
4938       myLastCreatedElems.Append(aNewElem);
4939       srcElements.Append( elem );
4940     }
4941
4942     // set new prev nodes
4943     for ( iNode = 0; iNode < nbNodes; iNode++ )
4944       prevNod[ iNode ] = nextNod[ iNode ];
4945
4946   } // loop on steps
4947 }
4948
4949 //=======================================================================
4950 /*!
4951  * \brief Create 1D and 2D elements around swept elements
4952  * \param mapNewNodes - source nodes and ones generated from them
4953  * \param newElemsMap - source elements and ones generated from them
4954  * \param elemNewNodesMap - nodes generated from each node of each element
4955  * \param elemSet - all swept elements
4956  * \param nbSteps - number of sweeping steps
4957  * \param srcElements - to append elem for each generated element
4958  */
4959 //=======================================================================
4960
4961 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap &     mapNewNodes,
4962                                   TTElemOfElemListMap &    newElemsMap,
4963                                   TElemOfVecOfNnlmiMap &   elemNewNodesMap,
4964                                   TIDSortedElemSet&        elemSet,
4965                                   const int                nbSteps,
4966                                   SMESH_SequenceOfElemPtr& srcElements)
4967 {
4968   ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4969   SMESHDS_Mesh* aMesh = GetMeshDS();
4970
4971   // Find nodes belonging to only one initial element - sweep them into edges.
4972
4973   TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4974   for ( ; nList != mapNewNodes.end(); nList++ )
4975   {
4976     const SMDS_MeshNode* node =
4977       static_cast<const SMDS_MeshNode*>( nList->first );
4978     if ( newElemsMap.count( node ))
4979       continue; // node was extruded into edge
4980     SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4981     int nbInitElems = 0;
4982     const SMDS_MeshElement* el = 0;
4983     SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4984     while ( eIt->more() && nbInitElems < 2 ) {
4985       const SMDS_MeshElement* e = eIt->next();
4986       SMDSAbs_ElementType  type = e->GetType();
4987       if ( type == SMDSAbs_Volume ||
4988            type < highType ||
4989            !elemSet.count(e))
4990         continue;
4991       if ( type > highType ) {
4992         nbInitElems = 0;
4993         highType    = type;
4994       }
4995       el = e;
4996       ++nbInitElems;
4997     }
4998     if ( nbInitElems == 1 ) {
4999       bool NotCreateEdge = el && el->IsMediumNode(node);
5000       if(!NotCreateEdge) {
5001         vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
5002         list<const SMDS_MeshElement*> newEdges;
5003         sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
5004       }
5005     }
5006   }
5007
5008   // Make a ceiling for each element ie an equal element of last new nodes.
5009   // Find free links of faces - make edges and sweep them into faces.
5010
5011   ElemFeatures polyFace( SMDSAbs_Face, /*isPoly=*/true ), anyFace;
5012
5013   TTElemOfElemListMap::iterator  itElem      = newElemsMap.begin();
5014   TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
5015   for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
5016   {
5017     const SMDS_MeshElement* elem = itElem->first;
5018     vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
5019
5020     if(itElem->second.size()==0) continue;
5021
5022     const bool isQuadratic = elem->IsQuadratic();
5023
5024     if ( elem->GetType() == SMDSAbs_Edge ) {
5025       // create a ceiling edge
5026       if ( !isQuadratic ) {
5027         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
5028                                vecNewNodes[ 1 ]->second.back())) {
5029           myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
5030                                                    vecNewNodes[ 1 ]->second.back()));
5031           srcElements.Append( elem );
5032         }
5033       }
5034       else {
5035         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
5036                                vecNewNodes[ 1 ]->second.back(),
5037                                vecNewNodes[ 2 ]->second.back())) {
5038           myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
5039                                                    vecNewNodes[ 1 ]->second.back(),
5040                                                    vecNewNodes[ 2 ]->second.back()));
5041           srcElements.Append( elem );
5042         }
5043       }
5044     }
5045     if ( elem->GetType() != SMDSAbs_Face )
5046       continue;
5047
5048     bool hasFreeLinks = false;
5049
5050     TIDSortedElemSet avoidSet;
5051     avoidSet.insert( elem );
5052
5053     set<const SMDS_MeshNode*> aFaceLastNodes;
5054     int iNode, nbNodes = vecNewNodes.size();
5055     if ( !isQuadratic ) {
5056       // loop on the face nodes
5057       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
5058         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5059         // look for free links of the face
5060         int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
5061         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
5062         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
5063         // check if a link n1-n2 is free
5064         if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
5065           hasFreeLinks = true;
5066           // make a new edge and a ceiling for a new edge
5067           const SMDS_MeshElement* edge;
5068           if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
5069             myLastCreatedElems.Append( edge = aMesh->AddEdge( n1, n2 )); // free link edge
5070             srcElements.Append( myLastCreatedElems.Last() );
5071           }
5072           n1 = vecNewNodes[ iNode ]->second.back();
5073           n2 = vecNewNodes[ iNext ]->second.back();
5074           if ( !aMesh->FindEdge( n1, n2 )) {
5075             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // new edge ceiling
5076             srcElements.Append( edge );
5077           }
5078         }
5079       }
5080     }
5081     else { // elem is quadratic face
5082       int nbn = nbNodes/2;
5083       for ( iNode = 0; iNode < nbn; iNode++ ) {
5084         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5085         int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
5086         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
5087         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
5088         const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
5089         // check if a link is free
5090         if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
5091              ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
5092              ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
5093           hasFreeLinks = true;
5094           // make an edge and a ceiling for a new edge
5095           // find medium node
5096           if ( !aMesh->FindEdge( n1, n2, n3 )) {
5097             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // free link edge
5098             srcElements.Append( elem );
5099           }
5100           n1 = vecNewNodes[ iNode ]->second.back();
5101           n2 = vecNewNodes[ iNext ]->second.back();
5102           n3 = vecNewNodes[ iNode+nbn ]->second.back();
5103           if ( !aMesh->FindEdge( n1, n2, n3 )) {
5104             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
5105             srcElements.Append( elem );
5106           }
5107         }
5108       }
5109       for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
5110         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5111       }
5112     }
5113
5114     // sweep free links into faces
5115
5116     if ( hasFreeLinks ) {
5117       list<const SMDS_MeshElement*> & newVolumes = itElem->second;
5118       int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
5119
5120       set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
5121       set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
5122       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
5123         initNodeSet.insert( vecNewNodes[ iNode ]->first );
5124         topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
5125       }
5126       if ( isQuadratic && nbNodes % 2 ) {  // node set for the case of a biquadratic
5127         initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
5128         initNodeSetNoCenter.erase( vecNewNodes.back()->first );
5129       }
5130       for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
5131         list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
5132         std::advance( v, volNb );
5133         // find indices of free faces of a volume and their source edges
5134         list< int > freeInd;
5135         list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
5136         SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
5137         int iF, nbF = vTool.NbFaces();
5138         for ( iF = 0; iF < nbF; iF ++ ) {
5139           if (vTool.IsFreeFace( iF ) &&
5140               vTool.GetFaceNodes( iF, faceNodeSet ) &&
5141               initNodeSet != faceNodeSet) // except an initial face
5142           {
5143             if ( nbSteps == 1 && faceNodeSet == topNodeSet )
5144               continue;
5145             if ( faceNodeSet == initNodeSetNoCenter )
5146               continue;
5147             freeInd.push_back( iF );
5148             // find source edge of a free face iF
5149             vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
5150             vector<const SMDS_MeshNode*>::iterator lastCommom;
5151             commonNodes.resize( nbNodes, 0 );
5152             lastCommom = std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
5153                                                 initNodeSet.begin(), initNodeSet.end(),
5154                                                 commonNodes.begin());
5155             if ( std::distance( commonNodes.begin(), lastCommom ) == 3 )
5156               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
5157             else
5158               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
5159 #ifdef _DEBUG_
5160             if ( !srcEdges.back() )
5161             {
5162               cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
5163                    << iF << " of volume #" << vTool.ID() << endl;
5164             }
5165 #endif
5166           }
5167         }
5168         if ( freeInd.empty() )
5169           continue;
5170
5171         // create wall faces for all steps;
5172         // if such a face has been already created by sweep of edge,
5173         // assure that its orientation is OK
5174         for ( int iStep = 0; iStep < nbSteps; iStep++ )
5175         {
5176           vTool.Set( *v, /*ignoreCentralNodes=*/false );
5177           vTool.SetExternalNormal();
5178           const int nextShift = vTool.IsForward() ? +1 : -1;
5179           list< int >::iterator ind = freeInd.begin();
5180           list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
5181           for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
5182           {
5183             const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
5184             int nbn = vTool.NbFaceNodes( *ind );
5185             const SMDS_MeshElement * f = 0;
5186             if ( nbn == 3 )              ///// triangle
5187             {
5188               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
5189               if ( !f ||
5190                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
5191               {
5192                 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
5193                                                      nodes[ 1 ],
5194                                                      nodes[ 1 + nextShift ] };
5195                 if ( f )
5196                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5197                 else
5198                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
5199                                                             newOrder[ 2 ] ));
5200               }
5201             }
5202             else if ( nbn == 4 )       ///// quadrangle
5203             {
5204               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
5205               if ( !f ||
5206                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
5207               {
5208                 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
5209                                                      nodes[ 2 ], nodes[ 2+nextShift ] };
5210                 if ( f )
5211                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5212                 else
5213                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
5214                                                             newOrder[ 2 ], newOrder[ 3 ]));
5215               }
5216             }
5217             else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
5218             {
5219               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
5220               if ( !f ||
5221                    nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
5222               {
5223                 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
5224                                                      nodes[2],
5225                                                      nodes[2 + 2*nextShift],
5226                                                      nodes[3 - 2*nextShift],
5227                                                      nodes[3],
5228                                                      nodes[3 + 2*nextShift]};
5229                 if ( f )
5230                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5231                 else
5232                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ],
5233                                                             newOrder[ 1 ],
5234                                                             newOrder[ 2 ],
5235                                                             newOrder[ 3 ],
5236                                                             newOrder[ 4 ],
5237                                                             newOrder[ 5 ] ));
5238               }
5239             }
5240             else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
5241             {
5242               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
5243                                    nodes[1], nodes[3], nodes[5], nodes[7] );
5244               if ( !f ||
5245                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5246               {
5247                 const SMDS_MeshNode* newOrder[8] = { nodes[0],
5248                                                      nodes[4 - 2*nextShift],
5249                                                      nodes[4],
5250                                                      nodes[4 + 2*nextShift],
5251                                                      nodes[1],
5252                                                      nodes[5 - 2*nextShift],
5253                                                      nodes[5],
5254                                                      nodes[5 + 2*nextShift] };
5255                 if ( f )
5256                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5257                 else
5258                   myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5259                                                            newOrder[ 2 ], newOrder[ 3 ],
5260                                                            newOrder[ 4 ], newOrder[ 5 ],
5261                                                            newOrder[ 6 ], newOrder[ 7 ]));
5262               }
5263             }
5264             else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
5265             {
5266               f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
5267                                       SMDSAbs_Face, /*noMedium=*/false);
5268               if ( !f ||
5269                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5270               {
5271                 const SMDS_MeshNode* newOrder[9] = { nodes[0],
5272                                                      nodes[4 - 2*nextShift],
5273                                                      nodes[4],
5274                                                      nodes[4 + 2*nextShift],
5275                                                      nodes[1],
5276                                                      nodes[5 - 2*nextShift],
5277                                                      nodes[5],
5278                                                      nodes[5 + 2*nextShift],
5279                                                      nodes[8] };
5280                 if ( f )
5281                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5282                 else
5283                   myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5284                                                            newOrder[ 2 ], newOrder[ 3 ],
5285                                                            newOrder[ 4 ], newOrder[ 5 ],
5286                                                            newOrder[ 6 ], newOrder[ 7 ],
5287                                                            newOrder[ 8 ]));
5288               }
5289             }
5290             else  //////// polygon
5291             {
5292               vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
5293               const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
5294               if ( !f ||
5295                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
5296               {
5297                 if ( !vTool.IsForward() )
5298                   std::reverse( polygon_nodes.begin(), polygon_nodes.end());
5299                 if ( f )
5300                   aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
5301                 else
5302                   AddElement( polygon_nodes, polyFace.SetQuad( (*v)->IsQuadratic() ));
5303               }
5304             }
5305
5306             while ( srcElements.Length() < myLastCreatedElems.Length() )
5307               srcElements.Append( *srcEdge );
5308
5309           }  // loop on free faces
5310
5311           // go to the next volume
5312           iVol = 0;
5313           while ( iVol++ < nbVolumesByStep ) v++;
5314
5315         } // loop on steps
5316       } // loop on volumes of one step
5317     } // sweep free links into faces
5318
5319     // Make a ceiling face with a normal external to a volume
5320
5321     // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
5322     SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
5323     int iF = lastVol.GetFaceIndex( aFaceLastNodes );
5324
5325     if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
5326       aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
5327       iF = lastVol.GetFaceIndex( aFaceLastNodes );
5328     }
5329     if ( iF >= 0 )
5330     {
5331       lastVol.SetExternalNormal();
5332       const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
5333       const               int nbn = lastVol.NbFaceNodes( iF );
5334       vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
5335       if ( !hasFreeLinks ||
5336            !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
5337       {
5338         const vector<int>& interlace =
5339           SMDS_MeshCell::interlacedSmdsOrder( elem->GetEntityType(), nbn );
5340         SMDS_MeshCell::applyInterlaceRev( interlace, nodeVec );
5341
5342         AddElement( nodeVec, anyFace.Init( elem ));
5343
5344         while ( srcElements.Length() < myLastCreatedElems.Length() )
5345           srcElements.Append( elem );
5346       }
5347     }
5348   } // loop on swept elements
5349 }
5350
5351 //=======================================================================
5352 //function : RotationSweep
5353 //purpose  :
5354 //=======================================================================
5355
5356 SMESH_MeshEditor::PGroupIDs
5357 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet   theElemSets[2],
5358                                 const gp_Ax1&      theAxis,
5359                                 const double       theAngle,
5360                                 const int          theNbSteps,
5361                                 const double       theTol,
5362                                 const bool         theMakeGroups,
5363                                 const bool         theMakeWalls)
5364 {
5365   myLastCreatedElems.Clear();
5366   myLastCreatedNodes.Clear();
5367
5368   // source elements for each generated one
5369   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5370
5371   gp_Trsf aTrsf;
5372   aTrsf.SetRotation( theAxis, theAngle );
5373   gp_Trsf aTrsf2;
5374   aTrsf2.SetRotation( theAxis, theAngle/2. );
5375
5376   gp_Lin aLine( theAxis );
5377   double aSqTol = theTol * theTol;
5378
5379   SMESHDS_Mesh* aMesh = GetMeshDS();
5380
5381   TNodeOfNodeListMap mapNewNodes;
5382   TElemOfVecOfNnlmiMap mapElemNewNodes;
5383   TTElemOfElemListMap newElemsMap;
5384
5385   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5386                                      myMesh->NbFaces(ORDER_QUADRATIC) +
5387                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
5388   // loop on theElemSets
5389   setElemsFirst( theElemSets );
5390   TIDSortedElemSet::iterator itElem;
5391   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5392   {
5393     TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5394     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5395       const SMDS_MeshElement* elem = *itElem;
5396       if ( !elem || elem->GetType() == SMDSAbs_Volume )
5397         continue;
5398       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5399       newNodesItVec.reserve( elem->NbNodes() );
5400
5401       // loop on elem nodes
5402       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5403       while ( itN->more() )
5404       {
5405         const SMDS_MeshNode* node = cast2Node( itN->next() );
5406
5407         gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
5408         double coord[3];
5409         aXYZ.Coord( coord[0], coord[1], coord[2] );
5410         bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
5411
5412         // check if a node has been already sweeped
5413         TNodeOfNodeListMapItr nIt =
5414           mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5415         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5416         if ( listNewNodes.empty() )
5417         {
5418           // check if we are to create medium nodes between corner ones
5419           bool needMediumNodes = false;
5420           if ( isQuadraticMesh )
5421           {
5422             SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5423             while (it->more() && !needMediumNodes )
5424             {
5425               const SMDS_MeshElement* invElem = it->next();
5426               if ( invElem != elem && !theElems.count( invElem )) continue;
5427               needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5428               if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5429                 needMediumNodes = true;
5430             }
5431           }
5432
5433           // make new nodes
5434           const SMDS_MeshNode * newNode = node;
5435           for ( int i = 0; i < theNbSteps; i++ ) {
5436             if ( !isOnAxis ) {
5437               if ( needMediumNodes )  // create a medium node
5438               {
5439                 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5440                 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5441                 myLastCreatedNodes.Append(newNode);
5442                 srcNodes.Append( node );
5443                 listNewNodes.push_back( newNode );
5444                 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5445               }
5446               else {
5447                 aTrsf.Transforms( coord[0], coord[1], coord[2] );
5448               }
5449               // create a corner node
5450               newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5451               myLastCreatedNodes.Append(newNode);
5452               srcNodes.Append( node );
5453               listNewNodes.push_back( newNode );
5454             }
5455             else {
5456               listNewNodes.push_back( newNode );
5457               // if ( needMediumNodes )
5458               //   listNewNodes.push_back( newNode );
5459             }
5460           }
5461         }
5462         newNodesItVec.push_back( nIt );
5463       }
5464       // make new elements
5465       sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
5466     }
5467   }
5468
5469   if ( theMakeWalls )
5470     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], theNbSteps, srcElems );
5471
5472   PGroupIDs newGroupIDs;
5473   if ( theMakeGroups )
5474     newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
5475
5476   return newGroupIDs;
5477 }
5478
5479 //=======================================================================
5480 //function : ExtrusParam
5481 //purpose  : standard construction
5482 //=======================================================================
5483
5484 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Vec&            theStep,
5485                                             const int                theNbSteps,
5486                                             const std::list<double>& theScales,
5487                                             const gp_XYZ*            theBasePoint,
5488                                             const int                theFlags,
5489                                             const double             theTolerance):
5490   myDir( theStep ),
5491   myBaseP( Precision::Infinite(), 0, 0 ),
5492   myFlags( theFlags ),
5493   myTolerance( theTolerance ),
5494   myElemsToUse( NULL )
5495 {
5496   mySteps = new TColStd_HSequenceOfReal;
5497   const double stepSize = theStep.Magnitude();
5498   for (int i=1; i<=theNbSteps; i++ )
5499     mySteps->Append( stepSize );
5500
5501   int nbScales = theScales.size();
5502   if ( nbScales > 0 )
5503   {
5504     if ( IsLinearVariation() && nbScales < theNbSteps )
5505     {
5506       myScales.reserve( theNbSteps );
5507       std::list<double>::const_iterator scale = theScales.begin();
5508       double prevScale = 1.0;
5509       for ( int iSc = 1; scale != theScales.end(); ++scale, ++iSc )
5510       {
5511         int      iStep = int( iSc / double( nbScales ) * theNbSteps + 0.5 );
5512         int    stDelta = Max( 1, iStep - myScales.size());
5513         double scDelta = ( *scale - prevScale ) / stDelta;
5514         for ( int iStep = 0; iStep < stDelta; ++iStep )
5515         {
5516           myScales.push_back( prevScale + scDelta );
5517           prevScale = myScales.back();
5518         }
5519         prevScale = *scale;
5520       }
5521     }
5522     else
5523     {
5524       myScales.assign( theScales.begin(), theScales.end() );
5525     }
5526   }
5527   if ( theBasePoint )
5528   {
5529     myBaseP = *theBasePoint;
5530   }
5531
5532   if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5533       ( theTolerance > 0 ))
5534   {
5535     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5536   }
5537   else
5538   {
5539     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5540   }
5541 }
5542
5543 //=======================================================================
5544 //function : ExtrusParam
5545 //purpose  : steps are given explicitly
5546 //=======================================================================
5547
5548 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Dir&                   theDir,
5549                                             Handle(TColStd_HSequenceOfReal) theSteps,
5550                                             const int                       theFlags,
5551                                             const double                    theTolerance):
5552   myDir( theDir ),
5553   mySteps( theSteps ),
5554   myFlags( theFlags ),
5555   myTolerance( theTolerance ),
5556   myElemsToUse( NULL )
5557 {
5558   if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5559       ( theTolerance > 0 ))
5560   {
5561     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5562   }
5563   else
5564   {
5565     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5566   }
5567 }
5568
5569 //=======================================================================
5570 //function : ExtrusParam
5571 //purpose  : for extrusion by normal
5572 //=======================================================================
5573
5574 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const double theStepSize,
5575                                             const int    theNbSteps,
5576                                             const int    theFlags,
5577                                             const int    theDim ):
5578   myDir( 1,0,0 ),
5579   mySteps( new TColStd_HSequenceOfReal ),
5580   myFlags( theFlags ),
5581   myTolerance( 0 ),
5582   myElemsToUse( NULL )
5583 {
5584   for (int i = 0; i < theNbSteps; i++ )
5585     mySteps->Append( theStepSize );
5586
5587   if ( theDim == 1 )
5588   {
5589     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal1D;
5590   }
5591   else
5592   {
5593     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal2D;
5594   }
5595 }
5596
5597 //=======================================================================
5598 //function : ExtrusParam::SetElementsToUse
5599 //purpose  : stores elements to use for extrusion by normal, depending on
5600 //           state of EXTRUSION_FLAG_USE_INPUT_ELEMS_ONLY flag;
5601 //           define myBaseP for scaling
5602 //=======================================================================
5603
5604 void SMESH_MeshEditor::ExtrusParam::SetElementsToUse( const TIDSortedElemSet& elems,
5605                                                       const TIDSortedElemSet& nodes )
5606 {
5607   myElemsToUse = ToUseInpElemsOnly() ? & elems : 0;
5608
5609   if ( Precision::IsInfinite( myBaseP.X() )) // myBaseP not defined
5610   {
5611     myBaseP.SetCoord( 0.,0.,0. );
5612     TIDSortedElemSet newNodes;
5613
5614     const TIDSortedElemSet* elemSets[] = { &elems, &nodes };
5615     for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5616     {
5617       const TIDSortedElemSet& elements = *( elemSets[ is2ndSet ]);
5618       TIDSortedElemSet::const_iterator itElem = elements.begin();
5619       for ( ; itElem != elements.end(); itElem++ )
5620       {
5621         const SMDS_MeshElement* elem = *itElem;
5622         SMDS_ElemIteratorPtr     itN = elem->nodesIterator();
5623         while ( itN->more() ) {
5624           const SMDS_MeshElement* node = itN->next();
5625           if ( newNodes.insert( node ).second )
5626             myBaseP += SMESH_TNodeXYZ( node );
5627         }
5628       }
5629     }
5630     myBaseP /= newNodes.size();
5631   }
5632 }
5633
5634 //=======================================================================
5635 //function : ExtrusParam::beginStepIter
5636 //purpose  : prepare iteration on steps
5637 //=======================================================================
5638
5639 void SMESH_MeshEditor::ExtrusParam::beginStepIter( bool withMediumNodes )
5640 {
5641   myWithMediumNodes = withMediumNodes;
5642   myNextStep = 1;
5643   myCurSteps.clear();
5644 }
5645 //=======================================================================
5646 //function : ExtrusParam::moreSteps
5647 //purpose  : are there more steps?
5648 //=======================================================================
5649
5650 bool SMESH_MeshEditor::ExtrusParam::moreSteps()
5651 {
5652   return myNextStep <= mySteps->Length() || !myCurSteps.empty();
5653 }
5654 //=======================================================================
5655 //function : ExtrusParam::nextStep
5656 //purpose  : returns the next step
5657 //=======================================================================
5658
5659 double SMESH_MeshEditor::ExtrusParam::nextStep()
5660 {
5661   double res = 0;
5662   if ( !myCurSteps.empty() )
5663   {
5664     res = myCurSteps.back();
5665     myCurSteps.pop_back();
5666   }
5667   else if ( myNextStep <= mySteps->Length() )
5668   {
5669     myCurSteps.push_back( mySteps->Value( myNextStep ));
5670     ++myNextStep;
5671     if ( myWithMediumNodes )
5672     {
5673       myCurSteps.back() /= 2.;
5674       myCurSteps.push_back( myCurSteps.back() );
5675     }
5676     res = nextStep();
5677   }
5678   return res;
5679 }
5680
5681 //=======================================================================
5682 //function : ExtrusParam::makeNodesByDir
5683 //purpose  : create nodes for standard extrusion
5684 //=======================================================================
5685
5686 int SMESH_MeshEditor::ExtrusParam::
5687 makeNodesByDir( SMESHDS_Mesh*                     mesh,
5688                 const SMDS_MeshNode*              srcNode,
5689                 std::list<const SMDS_MeshNode*> & newNodes,
5690                 const bool                        makeMediumNodes)
5691 {
5692   gp_XYZ p = SMESH_TNodeXYZ( srcNode );
5693
5694   int nbNodes = 0;
5695   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5696   {
5697     p += myDir.XYZ() * nextStep();
5698     const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5699     newNodes.push_back( newNode );
5700   }
5701
5702   if ( !myScales.empty() )
5703   {
5704     if ( makeMediumNodes && myMediumScales.empty() )
5705     {
5706       myMediumScales.resize( myScales.size() );
5707       double prevFactor = 1.;
5708       for ( size_t i = 0; i < myScales.size(); ++i )
5709       {
5710         myMediumScales[i] = 0.5 * ( prevFactor + myScales[i] );
5711         prevFactor = myScales[i];
5712       }
5713     }
5714     typedef std::vector<double>::iterator ScaleIt;
5715     ScaleIt scales[] = { myScales.begin(), myMediumScales.begin() };
5716
5717     size_t iSc = 0, nbScales = myScales.size() + myMediumScales.size();
5718
5719     gp_XYZ center = myBaseP;
5720     std::list<const SMDS_MeshNode*>::iterator nIt = newNodes.begin();
5721     size_t iN  = 0;
5722     for ( beginStepIter( makeMediumNodes ); moreSteps() && ( iN < nbScales ); ++nIt, ++iN )
5723     {
5724       center += myDir.XYZ() * nextStep();
5725
5726       iSc += int( makeMediumNodes );
5727       ScaleIt& scale = scales[ iSc % 2 ];
5728       
5729       gp_XYZ xyz = SMESH_TNodeXYZ( *nIt );
5730       xyz = ( *scale * ( xyz - center )) + center;
5731       mesh->MoveNode( *nIt, xyz.X(), xyz.Y(), xyz.Z() );
5732
5733       ++scale;
5734     }
5735   }
5736   return nbNodes;
5737 }
5738
5739 //=======================================================================
5740 //function : ExtrusParam::makeNodesByDirAndSew
5741 //purpose  : create nodes for standard extrusion with sewing
5742 //=======================================================================
5743
5744 int SMESH_MeshEditor::ExtrusParam::
5745 makeNodesByDirAndSew( SMESHDS_Mesh*                     mesh,
5746                       const SMDS_MeshNode*              srcNode,
5747                       std::list<const SMDS_MeshNode*> & newNodes,
5748                       const bool                        makeMediumNodes)
5749 {
5750   gp_XYZ P1 = SMESH_TNodeXYZ( srcNode );
5751
5752   int nbNodes = 0;
5753   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5754   {
5755     P1 += myDir.XYZ() * nextStep();
5756
5757     // try to search in sequence of existing nodes
5758     // if myNodes.Length()>0 we 'nave to use given sequence
5759     // else - use all nodes of mesh
5760     const SMDS_MeshNode * node = 0;
5761     if ( myNodes.Length() > 0 ) {
5762       int i;
5763       for(i=1; i<=myNodes.Length(); i++) {
5764         gp_XYZ P2 = SMESH_TNodeXYZ( myNodes.Value(i) );
5765         if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5766         {
5767           node = myNodes.Value(i);
5768           break;
5769         }
5770       }
5771     }
5772     else {
5773       SMDS_NodeIteratorPtr itn = mesh->nodesIterator();
5774       while(itn->more()) {
5775         SMESH_TNodeXYZ P2( itn->next() );
5776         if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5777         {
5778           node = P2._node;
5779           break;
5780         }
5781       }
5782     }
5783
5784     if ( !node )
5785       node = mesh->AddNode( P1.X(), P1.Y(), P1.Z() );
5786
5787     newNodes.push_back( node );
5788
5789   } // loop on steps
5790
5791   return nbNodes;
5792 }
5793
5794 //=======================================================================
5795 //function : ExtrusParam::makeNodesByNormal2D
5796 //purpose  : create nodes for extrusion using normals of faces
5797 //=======================================================================
5798
5799 int SMESH_MeshEditor::ExtrusParam::
5800 makeNodesByNormal2D( SMESHDS_Mesh*                     mesh,
5801                      const SMDS_MeshNode*              srcNode,
5802                      std::list<const SMDS_MeshNode*> & newNodes,
5803                      const bool                        makeMediumNodes)
5804 {
5805   const bool alongAvgNorm = ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL );
5806
5807   gp_XYZ p = SMESH_TNodeXYZ( srcNode );
5808
5809   // get normals to faces sharing srcNode
5810   vector< gp_XYZ > norms, baryCenters;
5811   gp_XYZ norm, avgNorm( 0,0,0 );
5812   SMDS_ElemIteratorPtr faceIt = srcNode->GetInverseElementIterator( SMDSAbs_Face );
5813   while ( faceIt->more() )
5814   {
5815     const SMDS_MeshElement* face = faceIt->next();
5816     if ( myElemsToUse && !myElemsToUse->count( face ))
5817       continue;
5818     if ( SMESH_MeshAlgos::FaceNormal( face, norm, /*normalized=*/true ))
5819     {
5820       norms.push_back( norm );
5821       avgNorm += norm;
5822       if ( !alongAvgNorm )
5823       {
5824         gp_XYZ bc(0,0,0);
5825         int nbN = 0;
5826         for ( SMDS_ElemIteratorPtr nIt = face->nodesIterator(); nIt->more(); ++nbN )
5827           bc += SMESH_TNodeXYZ( nIt->next() );
5828         baryCenters.push_back( bc / nbN );
5829       }
5830     }
5831   }
5832
5833   if ( norms.empty() ) return 0;
5834
5835   double normSize = avgNorm.Modulus();
5836   if ( normSize < std::numeric_limits<double>::min() )
5837     return 0;
5838
5839   if ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL ) // extrude along avgNorm
5840   {
5841     myDir = avgNorm;
5842     return makeNodesByDir( mesh, srcNode, newNodes, makeMediumNodes );
5843   }
5844
5845   avgNorm /= normSize;
5846
5847   int nbNodes = 0;
5848   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5849   {
5850     gp_XYZ pNew = p;
5851     double stepSize = nextStep();
5852
5853     if ( norms.size() > 1 )
5854     {
5855       for ( size_t iF = 0; iF < norms.size(); ++iF ) // loop on faces
5856       {
5857         // translate plane of a face
5858         baryCenters[ iF ] += norms[ iF ] * stepSize;
5859
5860         // find point of intersection of the face plane located at baryCenters[ iF ]
5861         // and avgNorm located at pNew
5862         double d    = -( norms[ iF ] * baryCenters[ iF ]); // d of plane equation ax+by+cz+d=0
5863         double dot  = ( norms[ iF ] * avgNorm );
5864         if ( dot < std::numeric_limits<double>::min() )
5865           dot = stepSize * 1e-3;
5866         double step = -( norms[ iF ] * pNew + d ) / dot;
5867         pNew += step * avgNorm;
5868       }
5869     }
5870     else
5871     {
5872       pNew += stepSize * avgNorm;
5873     }
5874     p = pNew;
5875
5876     const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5877     newNodes.push_back( newNode );
5878   }
5879   return nbNodes;
5880 }
5881
5882 //=======================================================================
5883 //function : ExtrusParam::makeNodesByNormal1D
5884 //purpose  : create nodes for extrusion using normals of edges
5885 //=======================================================================
5886
5887 int SMESH_MeshEditor::ExtrusParam::
5888 makeNodesByNormal1D( SMESHDS_Mesh*                     mesh,
5889                      const SMDS_MeshNode*              srcNode,
5890                      std::list<const SMDS_MeshNode*> & newNodes,
5891                      const bool                        makeMediumNodes)
5892 {
5893   throw SALOME_Exception("Extrusion 1D by Normal not implemented");
5894   return 0;
5895 }
5896
5897 //=======================================================================
5898 //function : ExtrusionSweep
5899 //purpose  :
5900 //=======================================================================
5901
5902 SMESH_MeshEditor::PGroupIDs
5903 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet     theElems[2],
5904                                   const gp_Vec&        theStep,
5905                                   const int            theNbSteps,
5906                                   TTElemOfElemListMap& newElemsMap,
5907                                   const int            theFlags,
5908                                   const double         theTolerance)
5909 {
5910   ExtrusParam aParams( theStep, theNbSteps, std::list<double>(), 0, theFlags, theTolerance );
5911   return ExtrusionSweep( theElems, aParams, newElemsMap );
5912 }
5913
5914
5915 //=======================================================================
5916 //function : ExtrusionSweep
5917 //purpose  :
5918 //=======================================================================
5919
5920 SMESH_MeshEditor::PGroupIDs
5921 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet     theElemSets[2],
5922                                   ExtrusParam&         theParams,
5923                                   TTElemOfElemListMap& newElemsMap)
5924 {
5925   myLastCreatedElems.Clear();
5926   myLastCreatedNodes.Clear();
5927
5928   // source elements for each generated one
5929   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5930
5931   setElemsFirst( theElemSets );
5932   const int nbSteps = theParams.NbSteps();
5933   theParams.SetElementsToUse( theElemSets[0], theElemSets[1] );
5934
5935   TNodeOfNodeListMap   mapNewNodes;
5936   TElemOfVecOfNnlmiMap mapElemNewNodes;
5937
5938   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5939                                      myMesh->NbFaces(ORDER_QUADRATIC) +
5940                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
5941   // loop on theElems
5942   TIDSortedElemSet::iterator itElem;
5943   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5944   {
5945     TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5946     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5947     {
5948       // check element type
5949       const SMDS_MeshElement* elem = *itElem;
5950       if ( !elem  || elem->GetType() == SMDSAbs_Volume )
5951         continue;
5952
5953       const size_t nbNodes = elem->NbNodes();
5954       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5955       newNodesItVec.reserve( nbNodes );
5956
5957       // loop on elem nodes
5958       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5959       while ( itN->more() )
5960       {
5961         // check if a node has been already sweeped
5962         const SMDS_MeshNode* node = cast2Node( itN->next() );
5963         TNodeOfNodeListMap::iterator nIt =
5964           mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5965         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5966         if ( listNewNodes.empty() )
5967         {
5968           // make new nodes
5969
5970           // check if we are to create medium nodes between corner ones
5971           bool needMediumNodes = false;
5972           if ( isQuadraticMesh )
5973           {
5974             SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5975             while (it->more() && !needMediumNodes )
5976             {
5977               const SMDS_MeshElement* invElem = it->next();
5978               if ( invElem != elem && !theElems.count( invElem )) continue;
5979               needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5980               if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5981                 needMediumNodes = true;
5982             }
5983           }
5984           // create nodes for all steps
5985           if ( theParams.MakeNodes( GetMeshDS(), node, listNewNodes, needMediumNodes ))
5986           {
5987             list<const SMDS_MeshNode*>::iterator newNodesIt = listNewNodes.begin();
5988             for ( ; newNodesIt != listNewNodes.end(); ++newNodesIt )
5989             {
5990               myLastCreatedNodes.Append( *newNodesIt );
5991               srcNodes.Append( node );
5992             }
5993           }
5994           else
5995           {
5996             break; // newNodesItVec will be shorter than nbNodes
5997           }
5998         }
5999         newNodesItVec.push_back( nIt );
6000       }
6001       // make new elements
6002       if ( newNodesItVec.size() == nbNodes )
6003         sweepElement( elem, newNodesItVec, newElemsMap[elem], nbSteps, srcElems );
6004     }
6005   }
6006
6007   if ( theParams.ToMakeBoundary() ) {
6008     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], nbSteps, srcElems );
6009   }
6010   PGroupIDs newGroupIDs;
6011   if ( theParams.ToMakeGroups() )
6012     newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
6013
6014   return newGroupIDs;
6015 }
6016
6017 //=======================================================================
6018 //function : ExtrusionAlongTrack
6019 //purpose  :
6020 //=======================================================================
6021 SMESH_MeshEditor::Extrusion_Error
6022 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet     theElements[2],
6023                                        SMESH_subMesh*       theTrack,
6024                                        const SMDS_MeshNode* theN1,
6025                                        const bool           theHasAngles,
6026                                        list<double>&        theAngles,
6027                                        const bool           theLinearVariation,
6028                                        const bool           theHasRefPoint,
6029                                        const gp_Pnt&        theRefPoint,
6030                                        const bool           theMakeGroups)
6031 {
6032   myLastCreatedElems.Clear();
6033   myLastCreatedNodes.Clear();
6034
6035   int aNbE;
6036   std::list<double> aPrms;
6037   TIDSortedElemSet::iterator itElem;
6038
6039   gp_XYZ aGC;
6040   TopoDS_Edge aTrackEdge;
6041   TopoDS_Vertex aV1, aV2;
6042
6043   SMDS_ElemIteratorPtr aItE;
6044   SMDS_NodeIteratorPtr aItN;
6045   SMDSAbs_ElementType aTypeE;
6046
6047   TNodeOfNodeListMap mapNewNodes;
6048
6049   // 1. Check data
6050   aNbE = theElements[0].size() + theElements[1].size();
6051   // nothing to do
6052   if ( !aNbE )
6053     return EXTR_NO_ELEMENTS;
6054
6055   // 1.1 Track Pattern
6056   ASSERT( theTrack );
6057
6058   SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS();
6059   if ( !pSubMeshDS )
6060     return ExtrusionAlongTrack( theElements, theTrack->GetFather(), theN1,
6061                                 theHasAngles, theAngles, theLinearVariation,
6062                                 theHasRefPoint, theRefPoint, theMakeGroups );
6063
6064   aItE = pSubMeshDS->GetElements();
6065   while ( aItE->more() ) {
6066     const SMDS_MeshElement* pE = aItE->next();
6067     aTypeE = pE->GetType();
6068     // Pattern must contain links only
6069     if ( aTypeE != SMDSAbs_Edge )
6070       return EXTR_PATH_NOT_EDGE;
6071   }
6072
6073   list<SMESH_MeshEditor_PathPoint> fullList;
6074
6075   const TopoDS_Shape& aS = theTrack->GetSubShape();
6076   // Sub-shape for the Pattern must be an Edge or Wire
6077   if( aS.ShapeType() == TopAbs_EDGE ) {
6078     aTrackEdge = TopoDS::Edge( aS );
6079     // the Edge must not be degenerated
6080     if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
6081       return EXTR_BAD_PATH_SHAPE;
6082     TopExp::Vertices( aTrackEdge, aV1, aV2 );
6083     aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
6084     const SMDS_MeshNode* aN1 = aItN->next();
6085     aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
6086     const SMDS_MeshNode* aN2 = aItN->next();
6087     // starting node must be aN1 or aN2
6088     if ( !( aN1 == theN1 || aN2 == theN1 ) )
6089       return EXTR_BAD_STARTING_NODE;
6090     aItN = pSubMeshDS->GetNodes();
6091     while ( aItN->more() ) {
6092       const SMDS_MeshNode* pNode = aItN->next();
6093       const SMDS_EdgePosition* pEPos =
6094         static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6095       double aT = pEPos->GetUParameter();
6096       aPrms.push_back( aT );
6097     }
6098     //Extrusion_Error err =
6099     makeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
6100   } else if( aS.ShapeType() == TopAbs_WIRE ) {
6101     list< SMESH_subMesh* > LSM;
6102     TopTools_SequenceOfShape Edges;
6103     SMESH_subMeshIteratorPtr itSM = theTrack->getDependsOnIterator(false,true);
6104     while(itSM->more()) {
6105       SMESH_subMesh* SM = itSM->next();
6106       LSM.push_back(SM);
6107       const TopoDS_Shape& aS = SM->GetSubShape();
6108       Edges.Append(aS);
6109     }
6110     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6111     int startNid = theN1->GetID();
6112     TColStd_MapOfInteger UsedNums;
6113
6114     int NbEdges = Edges.Length();
6115     int i = 1;
6116     for(; i<=NbEdges; i++) {
6117       int k = 0;
6118       list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
6119       for(; itLSM!=LSM.end(); itLSM++) {
6120         k++;
6121         if(UsedNums.Contains(k)) continue;
6122         aTrackEdge = TopoDS::Edge( Edges.Value(k) );
6123         SMESH_subMesh* locTrack = *itLSM;
6124         SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
6125         TopExp::Vertices( aTrackEdge, aV1, aV2 );
6126         aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
6127         const SMDS_MeshNode* aN1 = aItN->next();
6128         aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
6129         const SMDS_MeshNode* aN2 = aItN->next();
6130         // starting node must be aN1 or aN2
6131         if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
6132         // 2. Collect parameters on the track edge
6133         aPrms.clear();
6134         aItN = locMeshDS->GetNodes();
6135         while ( aItN->more() ) {
6136           const SMDS_MeshNode* pNode = aItN->next();
6137           const SMDS_EdgePosition* pEPos =
6138             static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6139           double aT = pEPos->GetUParameter();
6140           aPrms.push_back( aT );
6141         }
6142         list<SMESH_MeshEditor_PathPoint> LPP;
6143         //Extrusion_Error err =
6144         makeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
6145         LLPPs.push_back(LPP);
6146         UsedNums.Add(k);
6147         // update startN for search following egde
6148         if( aN1->GetID() == startNid ) startNid = aN2->GetID();
6149         else startNid = aN1->GetID();
6150         break;
6151       }
6152     }
6153     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6154     list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
6155     list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
6156     for(; itPP!=firstList.end(); itPP++) {
6157       fullList.push_back( *itPP );
6158     }
6159     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6160     fullList.pop_back();
6161     itLLPP++;
6162     for(; itLLPP!=LLPPs.end(); itLLPP++) {
6163       list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
6164       itPP = currList.begin();
6165       SMESH_MeshEditor_PathPoint PP2 = currList.front();
6166       gp_Dir D1 = PP1.Tangent();
6167       gp_Dir D2 = PP2.Tangent();
6168       gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
6169                            (D1.Z()+D2.Z())/2 ) );
6170       PP1.SetTangent(Dnew);
6171       fullList.push_back(PP1);
6172       itPP++;
6173       for(; itPP!=firstList.end(); itPP++) {
6174         fullList.push_back( *itPP );
6175       }
6176       PP1 = fullList.back();
6177       fullList.pop_back();
6178     }
6179     // if wire not closed
6180     fullList.push_back(PP1);
6181     // else ???
6182   }
6183   else {
6184     return EXTR_BAD_PATH_SHAPE;
6185   }
6186
6187   return makeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
6188                           theHasRefPoint, theRefPoint, theMakeGroups);
6189 }
6190
6191
6192 //=======================================================================
6193 //function : ExtrusionAlongTrack
6194 //purpose  :
6195 //=======================================================================
6196 SMESH_MeshEditor::Extrusion_Error
6197 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet     theElements[2],
6198                                        SMESH_Mesh*          theTrack,
6199                                        const SMDS_MeshNode* theN1,
6200                                        const bool           theHasAngles,
6201                                        list<double>&        theAngles,
6202                                        const bool           theLinearVariation,
6203                                        const bool           theHasRefPoint,
6204                                        const gp_Pnt&        theRefPoint,
6205                                        const bool           theMakeGroups)
6206 {
6207   myLastCreatedElems.Clear();
6208   myLastCreatedNodes.Clear();
6209
6210   int aNbE;
6211   std::list<double> aPrms;
6212   TIDSortedElemSet::iterator itElem;
6213
6214   gp_XYZ aGC;
6215   TopoDS_Edge aTrackEdge;
6216   TopoDS_Vertex aV1, aV2;
6217
6218   SMDS_ElemIteratorPtr aItE;
6219   SMDS_NodeIteratorPtr aItN;
6220   SMDSAbs_ElementType aTypeE;
6221
6222   TNodeOfNodeListMap mapNewNodes;
6223
6224   // 1. Check data
6225   aNbE = theElements[0].size() + theElements[1].size();
6226   // nothing to do
6227   if ( !aNbE )
6228     return EXTR_NO_ELEMENTS;
6229
6230   // 1.1 Track Pattern
6231   ASSERT( theTrack );
6232
6233   SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS();
6234
6235   aItE = pMeshDS->elementsIterator();
6236   while ( aItE->more() ) {
6237     const SMDS_MeshElement* pE = aItE->next();
6238     aTypeE = pE->GetType();
6239     // Pattern must contain links only
6240     if ( aTypeE != SMDSAbs_Edge )
6241       return EXTR_PATH_NOT_EDGE;
6242   }
6243
6244   list<SMESH_MeshEditor_PathPoint> fullList;
6245
6246   const TopoDS_Shape& aS = theTrack->GetShapeToMesh();
6247
6248   if ( !theTrack->HasShapeToMesh() ) {
6249     //Mesh without shape
6250     const SMDS_MeshNode* currentNode = NULL;
6251     const SMDS_MeshNode* prevNode = theN1;
6252     std::vector<const SMDS_MeshNode*> aNodesList;
6253     aNodesList.push_back(theN1);
6254     int nbEdges = 0, conn=0;
6255     const SMDS_MeshElement* prevElem = NULL;
6256     const SMDS_MeshElement* currentElem = NULL;
6257     int totalNbEdges = theTrack->NbEdges();
6258     SMDS_ElemIteratorPtr nIt;
6259
6260     //check start node
6261     if( !theTrack->GetMeshDS()->Contains(theN1) ) {
6262       return EXTR_BAD_STARTING_NODE;
6263     }
6264
6265     conn = nbEdgeConnectivity(theN1);
6266     if( conn != 1 )
6267       return EXTR_PATH_NOT_EDGE;
6268
6269     aItE = theN1->GetInverseElementIterator();
6270     prevElem = aItE->next();
6271     currentElem = prevElem;
6272     //Get all nodes
6273     if(totalNbEdges == 1 ) {
6274       nIt = currentElem->nodesIterator();
6275       currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6276       if(currentNode == prevNode)
6277         currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6278       aNodesList.push_back(currentNode);
6279     } else {
6280       nIt = currentElem->nodesIterator();
6281       while( nIt->more() ) {
6282         currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6283         if(currentNode == prevNode)
6284           currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6285         aNodesList.push_back(currentNode);
6286
6287         //case of the closed mesh
6288         if(currentNode == theN1) {
6289           nbEdges++;
6290           break;
6291         }
6292
6293         conn = nbEdgeConnectivity(currentNode);
6294         if(conn > 2) {
6295           return EXTR_PATH_NOT_EDGE;
6296         }else if( conn == 1 && nbEdges > 0 ) {
6297           //End of the path
6298           nbEdges++;
6299           break;
6300         }else {
6301           prevNode = currentNode;
6302           aItE = currentNode->GetInverseElementIterator();
6303           currentElem = aItE->next();
6304           if( currentElem  == prevElem)
6305             currentElem = aItE->next();
6306           nIt = currentElem->nodesIterator();
6307           prevElem = currentElem;
6308           nbEdges++;
6309         }
6310       }
6311     }
6312
6313     if(nbEdges != totalNbEdges)
6314       return EXTR_PATH_NOT_EDGE;
6315
6316     TopTools_SequenceOfShape Edges;
6317     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6318     int startNid = theN1->GetID();
6319     for ( size_t i = 1; i < aNodesList.size(); i++ )
6320     {
6321       gp_Pnt     p1 = SMESH_TNodeXYZ( aNodesList[i-1] );
6322       gp_Pnt     p2 = SMESH_TNodeXYZ( aNodesList[i] );
6323       TopoDS_Edge e = BRepBuilderAPI_MakeEdge( p1, p2 );
6324       list<SMESH_MeshEditor_PathPoint> LPP;
6325       aPrms.clear();
6326       makeEdgePathPoints(aPrms, e, (aNodesList[i-1]->GetID()==startNid), LPP);
6327       LLPPs.push_back(LPP);
6328       if ( aNodesList[i-1]->GetID() == startNid ) startNid = aNodesList[i  ]->GetID();
6329       else                                        startNid = aNodesList[i-1]->GetID();
6330     }
6331
6332     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6333     list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
6334     list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
6335     for(; itPP!=firstList.end(); itPP++) {
6336       fullList.push_back( *itPP );
6337     }
6338
6339     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6340     SMESH_MeshEditor_PathPoint PP2;
6341     fullList.pop_back();
6342     itLLPP++;
6343     for(; itLLPP!=LLPPs.end(); itLLPP++) {
6344       list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
6345       itPP = currList.begin();
6346       PP2 = currList.front();
6347       gp_Dir D1 = PP1.Tangent();
6348       gp_Dir D2 = PP2.Tangent();
6349       gp_Dir Dnew( 0.5 * ( D1.XYZ() + D2.XYZ() ));
6350       PP1.SetTangent(Dnew);
6351       fullList.push_back(PP1);
6352       itPP++;
6353       for(; itPP!=currList.end(); itPP++) {
6354         fullList.push_back( *itPP );
6355       }
6356       PP1 = fullList.back();
6357       fullList.pop_back();
6358     }
6359     fullList.push_back(PP1);
6360
6361   } // Sub-shape for the Pattern must be an Edge or Wire
6362   else if ( aS.ShapeType() == TopAbs_EDGE )
6363   {
6364     aTrackEdge = TopoDS::Edge( aS );
6365     // the Edge must not be degenerated
6366     if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
6367       return EXTR_BAD_PATH_SHAPE;
6368     TopExp::Vertices( aTrackEdge, aV1, aV2 );
6369     const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6370     const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6371     // starting node must be aN1 or aN2
6372     if ( !( aN1 == theN1 || aN2 == theN1 ) )
6373       return EXTR_BAD_STARTING_NODE;
6374     aItN = pMeshDS->nodesIterator();
6375     while ( aItN->more() ) {
6376       const SMDS_MeshNode* pNode = aItN->next();
6377       if( pNode==aN1 || pNode==aN2 ) continue;
6378       const SMDS_EdgePosition* pEPos =
6379         static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6380       double aT = pEPos->GetUParameter();
6381       aPrms.push_back( aT );
6382     }
6383     //Extrusion_Error err =
6384     makeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
6385   }
6386   else if( aS.ShapeType() == TopAbs_WIRE ) {
6387     list< SMESH_subMesh* > LSM;
6388     TopTools_SequenceOfShape Edges;
6389     TopExp_Explorer eExp(aS, TopAbs_EDGE);
6390     for(; eExp.More(); eExp.Next()) {
6391       TopoDS_Edge E = TopoDS::Edge( eExp.Current() );
6392       if( SMESH_Algo::isDegenerated(E) ) continue;
6393       SMESH_subMesh* SM = theTrack->GetSubMesh(E);
6394       if(SM) {
6395         LSM.push_back(SM);
6396         Edges.Append(E);
6397       }
6398     }
6399     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6400     TopoDS_Vertex aVprev;
6401     TColStd_MapOfInteger UsedNums;
6402     int NbEdges = Edges.Length();
6403     int i = 1;
6404     for(; i<=NbEdges; i++) {
6405       int k = 0;
6406       list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
6407       for(; itLSM!=LSM.end(); itLSM++) {
6408         k++;
6409         if(UsedNums.Contains(k)) continue;
6410         aTrackEdge = TopoDS::Edge( Edges.Value(k) );
6411         SMESH_subMesh* locTrack = *itLSM;
6412         SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
6413         TopExp::Vertices( aTrackEdge, aV1, aV2 );
6414         bool aN1isOK = false, aN2isOK = false;
6415         if ( aVprev.IsNull() ) {
6416           // if previous vertex is not yet defined, it means that we in the beginning of wire
6417           // and we have to find initial vertex corresponding to starting node theN1
6418           const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6419           const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6420           // starting node must be aN1 or aN2
6421           aN1isOK = ( aN1 && aN1 == theN1 );
6422           aN2isOK = ( aN2 && aN2 == theN1 );
6423         }
6424         else {
6425           // we have specified ending vertex of the previous edge on the previous iteration
6426           // and we have just to check that it corresponds to any vertex in current segment
6427           aN1isOK = aVprev.IsSame( aV1 );
6428           aN2isOK = aVprev.IsSame( aV2 );
6429         }
6430         if ( !aN1isOK && !aN2isOK ) continue;
6431         // 2. Collect parameters on the track edge
6432         aPrms.clear();
6433         aItN = locMeshDS->GetNodes();
6434         while ( aItN->more() ) {
6435           const SMDS_MeshNode*     pNode = aItN->next();
6436           const SMDS_EdgePosition* pEPos =
6437             static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6438           double aT = pEPos->GetUParameter();
6439           aPrms.push_back( aT );
6440         }
6441         list<SMESH_MeshEditor_PathPoint> LPP;
6442         //Extrusion_Error err =
6443         makeEdgePathPoints(aPrms, aTrackEdge, aN1isOK, LPP);
6444         LLPPs.push_back(LPP);
6445         UsedNums.Add(k);
6446         // update startN for search following egde
6447         if ( aN1isOK ) aVprev = aV2;
6448         else           aVprev = aV1;
6449         break;
6450       }
6451     }
6452     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6453     list<SMESH_MeshEditor_PathPoint>& firstList = *itLLPP;
6454     fullList.splice( fullList.end(), firstList );
6455
6456     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6457     fullList.pop_back();
6458     itLLPP++;
6459     for(; itLLPP!=LLPPs.end(); itLLPP++) {
6460       list<SMESH_MeshEditor_PathPoint>& currList = *itLLPP;
6461       SMESH_MeshEditor_PathPoint PP2 = currList.front();
6462       gp_Dir D1 = PP1.Tangent();
6463       gp_Dir D2 = PP2.Tangent();
6464       gp_Dir Dnew( D1.XYZ() + D2.XYZ() );
6465       PP1.SetTangent(Dnew);
6466       fullList.push_back(PP1);
6467       fullList.splice( fullList.end(), currList, ++currList.begin(), currList.end() );
6468       PP1 = fullList.back();
6469       fullList.pop_back();
6470     }
6471     // if wire not closed
6472     fullList.push_back(PP1);
6473     // else ???
6474   }
6475   else {
6476     return EXTR_BAD_PATH_SHAPE;
6477   }
6478
6479   return makeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
6480                           theHasRefPoint, theRefPoint, theMakeGroups);
6481 }
6482
6483
6484 //=======================================================================
6485 //function : makeEdgePathPoints
6486 //purpose  : auxiliary for ExtrusionAlongTrack
6487 //=======================================================================
6488 SMESH_MeshEditor::Extrusion_Error
6489 SMESH_MeshEditor::makeEdgePathPoints(std::list<double>&                aPrms,
6490                                      const TopoDS_Edge&                aTrackEdge,
6491                                      bool                              FirstIsStart,
6492                                      list<SMESH_MeshEditor_PathPoint>& LPP)
6493 {
6494   Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2;
6495   aTolVec=1.e-7;
6496   aTolVec2=aTolVec*aTolVec;
6497   double aT1, aT2;
6498   TopoDS_Vertex aV1, aV2;
6499   TopExp::Vertices( aTrackEdge, aV1, aV2 );
6500   aT1=BRep_Tool::Parameter( aV1, aTrackEdge );
6501   aT2=BRep_Tool::Parameter( aV2, aTrackEdge );
6502   // 2. Collect parameters on the track edge
6503   aPrms.push_front( aT1 );
6504   aPrms.push_back( aT2 );
6505   // sort parameters
6506   aPrms.sort();
6507   if( FirstIsStart ) {
6508     if ( aT1 > aT2 ) {
6509       aPrms.reverse();
6510     }
6511   }
6512   else {
6513     if ( aT2 > aT1 ) {
6514       aPrms.reverse();
6515     }
6516   }
6517   // 3. Path Points
6518   SMESH_MeshEditor_PathPoint aPP;
6519   Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 );
6520   std::list<double>::iterator aItD = aPrms.begin();
6521   for(; aItD != aPrms.end(); ++aItD) {
6522     double aT = *aItD;
6523     gp_Pnt aP3D;
6524     gp_Vec aVec;
6525     aC3D->D1( aT, aP3D, aVec );
6526     aL2 = aVec.SquareMagnitude();
6527     if ( aL2 < aTolVec2 )
6528       return EXTR_CANT_GET_TANGENT;
6529     gp_Dir aTgt( FirstIsStart ? aVec : -aVec );
6530     aPP.SetPnt( aP3D );
6531     aPP.SetTangent( aTgt );
6532     aPP.SetParameter( aT );
6533     LPP.push_back(aPP);
6534   }
6535   return EXTR_OK;
6536 }
6537
6538
6539 //=======================================================================
6540 //function : makeExtrElements
6541 //purpose  : auxiliary for ExtrusionAlongTrack
6542 //=======================================================================
6543 SMESH_MeshEditor::Extrusion_Error
6544 SMESH_MeshEditor::makeExtrElements(TIDSortedElemSet                  theElemSets[2],
6545                                    list<SMESH_MeshEditor_PathPoint>& fullList,
6546                                    const bool                        theHasAngles,
6547                                    list<double>&                     theAngles,
6548                                    const bool                        theLinearVariation,
6549                                    const bool                        theHasRefPoint,
6550                                    const gp_Pnt&                     theRefPoint,
6551                                    const bool                        theMakeGroups)
6552 {
6553   const int aNbTP = fullList.size();
6554
6555   // Angles
6556   if( theHasAngles && !theAngles.empty() && theLinearVariation )
6557     linearAngleVariation(aNbTP-1, theAngles);
6558
6559   // fill vector of path points with angles
6560   vector<SMESH_MeshEditor_PathPoint> aPPs;
6561   list<SMESH_MeshEditor_PathPoint>::iterator itPP = fullList.begin();
6562   list<double>::iterator                 itAngles = theAngles.begin();
6563   aPPs.push_back( *itPP++ );
6564   for( ; itPP != fullList.end(); itPP++) {
6565     aPPs.push_back( *itPP );
6566     if ( theHasAngles && itAngles != theAngles.end() )
6567       aPPs.back().SetAngle( *itAngles++ );
6568   }
6569
6570   TNodeOfNodeListMap   mapNewNodes;
6571   TElemOfVecOfNnlmiMap mapElemNewNodes;
6572   TTElemOfElemListMap  newElemsMap;
6573   TIDSortedElemSet::iterator itElem;
6574   // source elements for each generated one
6575   SMESH_SequenceOfElemPtr srcElems, srcNodes;
6576
6577   // 3. Center of rotation aV0
6578   gp_Pnt aV0 = theRefPoint;
6579   if ( !theHasRefPoint )
6580   {
6581     gp_XYZ aGC( 0.,0.,0. );
6582     TIDSortedElemSet newNodes;
6583
6584     for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6585     {
6586       TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6587       itElem = theElements.begin();
6588       for ( ; itElem != theElements.end(); itElem++ )
6589       {
6590         const SMDS_MeshElement* elem = *itElem;
6591         SMDS_ElemIteratorPtr     itN = elem->nodesIterator();
6592         while ( itN->more() ) {
6593           const SMDS_MeshElement* node = itN->next();
6594           if ( newNodes.insert( node ).second )
6595             aGC += SMESH_TNodeXYZ( node );
6596         }
6597       }
6598     }
6599     aGC /= newNodes.size();
6600     aV0.SetXYZ( aGC );
6601   } // if (!theHasRefPoint) {
6602
6603   // 4. Processing the elements
6604   SMESHDS_Mesh* aMesh = GetMeshDS();
6605   list<const SMDS_MeshNode*> emptyList;
6606
6607   setElemsFirst( theElemSets );
6608   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6609   {
6610     TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6611     for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ )
6612     {
6613       const SMDS_MeshElement* elem = *itElem;
6614
6615       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
6616       newNodesItVec.reserve( elem->NbNodes() );
6617
6618       // loop on elem nodes
6619       int nodeIndex = -1;
6620       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6621       while ( itN->more() )
6622       {
6623         ++nodeIndex;
6624         // check if a node has been already processed
6625         const SMDS_MeshNode* node = cast2Node( itN->next() );
6626         TNodeOfNodeListMap::iterator nIt = mapNewNodes.insert( make_pair( node, emptyList )).first;
6627         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
6628         if ( listNewNodes.empty() )
6629         {
6630           // make new nodes
6631           Standard_Real aAngle1x, aAngleT1T0, aTolAng;
6632           gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
6633           gp_Ax1 anAx1, anAxT1T0;
6634           gp_Dir aDT1x, aDT0x, aDT1T0;
6635
6636           aTolAng=1.e-4;
6637
6638           aV0x = aV0;
6639           aPN0 = SMESH_TNodeXYZ( node );
6640
6641           const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
6642           aP0x = aPP0.Pnt();
6643           aDT0x= aPP0.Tangent();
6644
6645           for ( int j = 1; j < aNbTP; ++j ) {
6646             const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
6647             aP1x     = aPP1.Pnt();
6648             aDT1x    = aPP1.Tangent();
6649             aAngle1x = aPP1.Angle();
6650
6651             gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
6652             // Translation
6653             gp_Vec aV01x( aP0x, aP1x );
6654             aTrsf.SetTranslation( aV01x );
6655
6656             // traslated point
6657             aV1x = aV0x.Transformed( aTrsf );
6658             aPN1 = aPN0.Transformed( aTrsf );
6659
6660             // rotation 1 [ T1,T0 ]
6661             aAngleT1T0=-aDT1x.Angle( aDT0x );
6662             if (fabs(aAngleT1T0) > aTolAng)
6663             {
6664               aDT1T0=aDT1x^aDT0x;
6665               anAxT1T0.SetLocation( aV1x );
6666               anAxT1T0.SetDirection( aDT1T0 );
6667               aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
6668
6669               aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
6670             }
6671
6672             // rotation 2
6673             if ( theHasAngles ) {
6674               anAx1.SetLocation( aV1x );
6675               anAx1.SetDirection( aDT1x );
6676               aTrsfRot.SetRotation( anAx1, aAngle1x );
6677
6678               aPN1 = aPN1.Transformed( aTrsfRot );
6679             }
6680
6681             // make new node
6682             if ( elem->IsQuadratic() && !elem->IsMediumNode(node) )
6683             {
6684               // create additional node
6685               gp_XYZ midP = 0.5 * ( aPN1.XYZ() + aPN0.XYZ() );
6686               const SMDS_MeshNode* newNode = aMesh->AddNode( midP.X(), midP.Y(), midP.Z() );
6687               myLastCreatedNodes.Append(newNode);
6688               srcNodes.Append( node );
6689               listNewNodes.push_back( newNode );
6690             }
6691             const SMDS_MeshNode* newNode = aMesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
6692             myLastCreatedNodes.Append(newNode);
6693             srcNodes.Append( node );
6694             listNewNodes.push_back( newNode );
6695
6696             aPN0 = aPN1;
6697             aP0x = aP1x;
6698             aV0x = aV1x;
6699             aDT0x = aDT1x;
6700           }
6701         }
6702         else if( elem->IsQuadratic() && !elem->IsMediumNode(node) )
6703         {
6704           // if current elem is quadratic and current node is not medium
6705           // we have to check - may be it is needed to insert additional nodes
6706           list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
6707           if ((int) listNewNodes.size() == aNbTP-1 )
6708           {
6709             vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
6710             gp_XYZ P(node->X(), node->Y(), node->Z());
6711             list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
6712             int i;
6713             for(i=0; i<aNbTP-1; i++) {
6714               const SMDS_MeshNode* N = *it;
6715               double x = ( N->X() + P.X() )/2.;
6716               double y = ( N->Y() + P.Y() )/2.;
6717               double z = ( N->Z() + P.Z() )/2.;
6718               const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
6719               srcNodes.Append( node );
6720               myLastCreatedNodes.Append(newN);
6721               aNodes[2*i] = newN;
6722               aNodes[2*i+1] = N;
6723               P = gp_XYZ(N->X(),N->Y(),N->Z());
6724             }
6725             listNewNodes.clear();
6726             for(i=0; i<2*(aNbTP-1); i++) {
6727               listNewNodes.push_back(aNodes[i]);
6728             }
6729           }
6730         }
6731
6732         newNodesItVec.push_back( nIt );
6733       }
6734
6735       // make new elements
6736       sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
6737     }
6738   }
6739
6740   makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], aNbTP-1, srcElems );
6741
6742   if ( theMakeGroups )
6743     generateGroups( srcNodes, srcElems, "extruded");
6744
6745   return EXTR_OK;
6746 }
6747
6748
6749 //=======================================================================
6750 //function : linearAngleVariation
6751 //purpose  : spread values over nbSteps
6752 //=======================================================================
6753
6754 void SMESH_MeshEditor::linearAngleVariation(const int     nbSteps,
6755                                             list<double>& Angles)
6756 {
6757   int nbAngles = Angles.size();
6758   if( nbSteps > nbAngles && nbAngles > 0 )
6759   {
6760     vector<double> theAngles(nbAngles);
6761     theAngles.assign( Angles.begin(), Angles.end() );
6762
6763     list<double> res;
6764     double rAn2St = double( nbAngles ) / double( nbSteps );
6765     double angPrev = 0, angle;
6766     for ( int iSt = 0; iSt < nbSteps; ++iSt )
6767     {
6768       double angCur = rAn2St * ( iSt+1 );
6769       double angCurFloor  = floor( angCur );
6770       double angPrevFloor = floor( angPrev );
6771       if ( angPrevFloor == angCurFloor )
6772         angle = rAn2St * theAngles[ int( angCurFloor ) ];
6773       else {
6774         int iP = int( angPrevFloor );
6775         double angPrevCeil = ceil(angPrev);
6776         angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6777
6778         int iC = int( angCurFloor );
6779         if ( iC < nbAngles )
6780           angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6781
6782         iP = int( angPrevCeil );
6783         while ( iC-- > iP )
6784           angle += theAngles[ iC ];
6785       }
6786       res.push_back(angle);
6787       angPrev = angCur;
6788     }
6789     Angles.swap( res );
6790   }
6791 }
6792
6793
6794 //================================================================================
6795 /*!
6796  * \brief Move or copy theElements applying theTrsf to their nodes
6797  *  \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6798  *  \param theTrsf - transformation to apply
6799  *  \param theCopy - if true, create translated copies of theElems
6800  *  \param theMakeGroups - if true and theCopy, create translated groups
6801  *  \param theTargetMesh - mesh to copy translated elements into
6802  *  \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6803  */
6804 //================================================================================
6805
6806 SMESH_MeshEditor::PGroupIDs
6807 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6808                              const gp_Trsf&     theTrsf,
6809                              const bool         theCopy,
6810                              const bool         theMakeGroups,
6811                              SMESH_Mesh*        theTargetMesh)
6812 {
6813   myLastCreatedElems.Clear();
6814   myLastCreatedNodes.Clear();
6815
6816   bool needReverse = false;
6817   string groupPostfix;
6818   switch ( theTrsf.Form() ) {
6819   case gp_PntMirror:
6820     needReverse = true;
6821     groupPostfix = "mirrored";
6822     break;
6823   case gp_Ax1Mirror:
6824     groupPostfix = "mirrored";
6825     break;
6826   case gp_Ax2Mirror:
6827     needReverse = true;
6828     groupPostfix = "mirrored";
6829     break;
6830   case gp_Rotation:
6831     groupPostfix = "rotated";
6832     break;
6833   case gp_Translation:
6834     groupPostfix = "translated";
6835     break;
6836   case gp_Scale:
6837     groupPostfix = "scaled";
6838     break;
6839   case gp_CompoundTrsf: // different scale by axis
6840     groupPostfix = "scaled";
6841     break;
6842   default:
6843     needReverse = false;
6844     groupPostfix = "transformed";
6845   }
6846
6847   SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6848   SMESHDS_Mesh* aMesh    = GetMeshDS();
6849
6850   SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6851   SMESH_MeshEditor* editor = theTargetMesh ? & targetMeshEditor : theCopy ? this : 0;
6852   SMESH_MeshEditor::ElemFeatures elemType;
6853
6854   // map old node to new one
6855   TNodeNodeMap nodeMap;
6856
6857   // elements sharing moved nodes; those of them which have all
6858   // nodes mirrored but are not in theElems are to be reversed
6859   TIDSortedElemSet inverseElemSet;
6860
6861   // source elements for each generated one
6862   SMESH_SequenceOfElemPtr srcElems, srcNodes;
6863
6864   // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6865   TIDSortedElemSet orphanNode;
6866
6867   if ( theElems.empty() ) // transform the whole mesh
6868   {
6869     // add all elements
6870     SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6871     while ( eIt->more() ) theElems.insert( eIt->next() );
6872     // add orphan nodes
6873     SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6874     while ( nIt->more() )
6875     {
6876       const SMDS_MeshNode* node = nIt->next();
6877       if ( node->NbInverseElements() == 0)
6878         orphanNode.insert( node );
6879     }
6880   }
6881
6882   // loop on elements to transform nodes : first orphan nodes then elems
6883   TIDSortedElemSet::iterator itElem;
6884   TIDSortedElemSet *elements[] = { &orphanNode, &theElems };
6885   for (int i=0; i<2; i++)
6886     for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ )
6887     {
6888       const SMDS_MeshElement* elem = *itElem;
6889       if ( !elem )
6890         continue;
6891
6892       // loop on elem nodes
6893       double coord[3];
6894       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6895       while ( itN->more() )
6896       {
6897         const SMDS_MeshNode* node = cast2Node( itN->next() );
6898         // check if a node has been already transformed
6899         pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6900           nodeMap.insert( make_pair ( node, node ));
6901         if ( !n2n_isnew.second )
6902           continue;
6903
6904         node->GetXYZ( coord );
6905         theTrsf.Transforms( coord[0], coord[1], coord[2] );
6906         if ( theTargetMesh ) {
6907           const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6908           n2n_isnew.first->second = newNode;
6909           myLastCreatedNodes.Append(newNode);
6910           srcNodes.Append( node );
6911         }
6912         else if ( theCopy ) {
6913           const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6914           n2n_isnew.first->second = newNode;
6915           myLastCreatedNodes.Append(newNode);
6916           srcNodes.Append( node );
6917         }
6918         else {
6919           aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6920           // node position on shape becomes invalid
6921           const_cast< SMDS_MeshNode* > ( node )->SetPosition
6922             ( SMDS_SpacePosition::originSpacePosition() );
6923         }
6924
6925         // keep inverse elements
6926         if ( !theCopy && !theTargetMesh && needReverse ) {
6927           SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6928           while ( invElemIt->more() ) {
6929             const SMDS_MeshElement* iel = invElemIt->next();
6930             inverseElemSet.insert( iel );
6931           }
6932         }
6933       }
6934     } // loop on elems in { &orphanNode, &theElems };
6935
6936   // either create new elements or reverse mirrored ones
6937   if ( !theCopy && !needReverse && !theTargetMesh )
6938     return PGroupIDs();
6939
6940   theElems.insert( inverseElemSet.begin(),inverseElemSet.end() );
6941
6942   // Replicate or reverse elements
6943
6944   std::vector<int> iForw;
6945   vector<const SMDS_MeshNode*> nodes;
6946   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6947   {
6948     const SMDS_MeshElement* elem = *itElem;
6949     if ( !elem ) continue;
6950
6951     SMDSAbs_GeometryType geomType = elem->GetGeomType();
6952     size_t               nbNodes  = elem->NbNodes();
6953     if ( geomType == SMDSGeom_NONE ) continue; // node
6954
6955     nodes.resize( nbNodes );
6956
6957     if ( geomType == SMDSGeom_POLYHEDRA )  // ------------------ polyhedral volume
6958     {
6959       const SMDS_VtkVolume* aPolyedre = dynamic_cast<const SMDS_VtkVolume*>( elem );
6960       if (!aPolyedre)
6961         continue;
6962       nodes.clear();
6963       bool allTransformed = true;
6964       int nbFaces = aPolyedre->NbFaces();
6965       for (int iface = 1; iface <= nbFaces && allTransformed; iface++)
6966       {
6967         int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6968         for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++)
6969         {
6970           const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6971           TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6972           if ( nodeMapIt == nodeMap.end() )
6973             allTransformed = false; // not all nodes transformed
6974           else
6975             nodes.push_back((*nodeMapIt).second);
6976         }
6977         if ( needReverse && allTransformed )
6978           std::reverse( nodes.end() - nbFaceNodes, nodes.end() );
6979       }
6980       if ( !allTransformed )
6981         continue; // not all nodes transformed
6982     }
6983     else // ----------------------- the rest element types
6984     {
6985       while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6986       const vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType(), nbNodes );
6987       const vector<int>&    i = needReverse ? iRev : iForw;
6988
6989       // find transformed nodes
6990       size_t iNode = 0;
6991       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6992       while ( itN->more() ) {
6993         const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
6994         TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6995         if ( nodeMapIt == nodeMap.end() )
6996           break; // not all nodes transformed
6997         nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
6998       }
6999       if ( iNode != nbNodes )
7000         continue; // not all nodes transformed
7001     }
7002
7003     if ( editor ) {
7004       // copy in this or a new mesh
7005       if ( editor->AddElement( nodes, elemType.Init( elem, /*basicOnly=*/false )))
7006         srcElems.Append( elem );
7007     }
7008     else {
7009       // reverse element as it was reversed by transformation
7010       if ( nbNodes > 2 )
7011         aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
7012     }
7013
7014   } // loop on elements
7015
7016   if ( editor && editor != this )
7017     myLastCreatedElems = editor->myLastCreatedElems;
7018
7019   PGroupIDs newGroupIDs;
7020
7021   if ( ( theMakeGroups && theCopy ) ||
7022        ( theMakeGroups && theTargetMesh ) )
7023     newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh, false );
7024
7025   return newGroupIDs;
7026 }
7027
7028 //=======================================================================
7029 /*!
7030  * \brief Create groups of elements made during transformation
7031  *  \param nodeGens - nodes making corresponding myLastCreatedNodes
7032  *  \param elemGens - elements making corresponding myLastCreatedElems
7033  *  \param postfix - to append to names of new groups
7034  *  \param targetMesh - mesh to create groups in
7035  *  \param topPresent - is there "top" elements that are created by sweeping
7036  */
7037 //=======================================================================
7038
7039 SMESH_MeshEditor::PGroupIDs
7040 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
7041                                  const SMESH_SequenceOfElemPtr& elemGens,
7042                                  const std::string&             postfix,
7043                                  SMESH_Mesh*                    targetMesh,
7044                                  const bool                     topPresent)
7045 {
7046   PGroupIDs newGroupIDs( new list<int> );
7047   SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
7048
7049   // Sort existing groups by types and collect their names
7050
7051   // containers to store an old group and generated new ones;
7052   // 1st new group is for result elems of different type than a source one;
7053   // 2nd new group is for same type result elems ("top" group at extrusion)
7054   using boost::tuple;
7055   using boost::make_tuple;
7056   typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
7057   vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
7058   vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
7059   // group names
7060   set< string > groupNames;
7061
7062   SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
7063   if ( !groupIt->more() ) return newGroupIDs;
7064
7065   int newGroupID = mesh->GetGroupIds().back()+1;
7066   while ( groupIt->more() )
7067   {
7068     SMESH_Group * group = groupIt->next();
7069     if ( !group ) continue;
7070     SMESHDS_GroupBase* groupDS = group->GetGroupDS();
7071     if ( !groupDS || groupDS->IsEmpty() ) continue;
7072     groupNames.insert    ( group->GetName() );
7073     groupDS->SetStoreName( group->GetName() );
7074     const SMDSAbs_ElementType type = groupDS->GetType();
7075     SMESHDS_Group* newGroup    = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
7076     SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
7077     groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
7078     orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
7079   }
7080
7081   // Loop on nodes and elements to add them in new groups
7082
7083   vector< const SMDS_MeshElement* > resultElems;
7084   for ( int isNodes = 0; isNodes < 2; ++isNodes )
7085   {
7086     const SMESH_SequenceOfElemPtr& gens  = isNodes ? nodeGens : elemGens;
7087     const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
7088     if ( gens.Length() != elems.Length() )
7089       throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
7090
7091     // loop on created elements
7092     for (int iElem = 1; iElem <= elems.Length(); ++iElem )
7093     {
7094       const SMDS_MeshElement* sourceElem = gens( iElem );
7095       if ( !sourceElem ) {
7096         MESSAGE("generateGroups(): NULL source element");
7097         continue;
7098       }
7099       list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
7100       if ( groupsOldNew.empty() ) { // no groups of this type at all
7101         while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
7102           ++iElem; // skip all elements made by sourceElem
7103         continue;
7104       }
7105       // collect all elements made by the iElem-th sourceElem
7106       resultElems.clear();
7107       if ( const SMDS_MeshElement* resElem = elems( iElem ))
7108         if ( resElem != sourceElem )
7109           resultElems.push_back( resElem );
7110       while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
7111         if ( const SMDS_MeshElement* resElem = elems( ++iElem ))
7112           if ( resElem != sourceElem )
7113             resultElems.push_back( resElem );
7114
7115       const SMDS_MeshElement* topElem = 0;
7116       if ( isNodes ) // there must be a top element
7117       {
7118         topElem = resultElems.back();
7119         resultElems.pop_back();
7120       }
7121       else
7122       {
7123         vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
7124         for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
7125           if ( (*resElemIt)->GetType() == sourceElem->GetType() )
7126           {
7127             topElem = *resElemIt;
7128             *resElemIt = 0; // erase *resElemIt
7129             break;
7130           }
7131       }
7132       // add resultElems to groups originted from ones the sourceElem belongs to
7133       list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
7134       for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
7135       {
7136         SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
7137         if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
7138         {
7139           // fill in a new group
7140           SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
7141           vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
7142           for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
7143             if ( *resElemIt )
7144               newGroup.Add( *resElemIt );
7145
7146           // fill a "top" group
7147           if ( topElem )
7148           {
7149             SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
7150             newTopGroup.Add( topElem );
7151          }
7152         }
7153       }
7154     } // loop on created elements
7155   }// loop on nodes and elements
7156
7157   // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
7158
7159   list<int> topGrouIds;
7160   for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
7161   {
7162     SMESHDS_GroupBase* oldGroupDS =   orderedOldNewGroups[i]->get<0>();
7163     SMESHDS_Group*   newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
7164                                       orderedOldNewGroups[i]->get<2>() };
7165     for ( int is2nd = 0; is2nd < 2; ++is2nd )
7166     {
7167       SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
7168       if ( newGroupDS->IsEmpty() )
7169       {
7170         mesh->GetMeshDS()->RemoveGroup( newGroupDS );
7171       }
7172       else
7173       {
7174         // set group type
7175         newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
7176
7177         // make a name
7178         const bool isTop = ( topPresent &&
7179                              newGroupDS->GetType() == oldGroupDS->GetType() &&
7180                              is2nd );
7181
7182         string name = oldGroupDS->GetStoreName();
7183         { // remove trailing whitespaces (issue 22599)
7184           size_t size = name.size();
7185           while ( size > 1 && isspace( name[ size-1 ]))
7186             --size;
7187           if ( size != name.size() )
7188           {
7189             name.resize( size );
7190             oldGroupDS->SetStoreName( name.c_str() );
7191           }
7192         }
7193         if ( !targetMesh ) {
7194           string suffix = ( isTop ? "top": postfix.c_str() );
7195           name += "_";
7196           name += suffix;
7197           int nb = 1;
7198           while ( !groupNames.insert( name ).second ) // name exists
7199             name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
7200         }
7201         else if ( isTop ) {
7202           name += "_top";
7203         }
7204         newGroupDS->SetStoreName( name.c_str() );
7205
7206         // make a SMESH_Groups
7207         mesh->AddGroup( newGroupDS );
7208         if ( isTop )
7209           topGrouIds.push_back( newGroupDS->GetID() );
7210         else
7211           newGroupIDs->push_back( newGroupDS->GetID() );
7212       }
7213     }
7214   }
7215   newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
7216
7217   return newGroupIDs;
7218 }
7219
7220 //================================================================================
7221 /*!
7222  *  * \brief Return list of group of nodes close to each other within theTolerance
7223  *  *        Search among theNodes or in the whole mesh if theNodes is empty using
7224  *  *        an Octree algorithm
7225  *  \param [in,out] theNodes - the nodes to treat
7226  *  \param [in]     theTolerance - the tolerance
7227  *  \param [out]    theGroupsOfNodes - the result groups of coincident nodes
7228  *  \param [in]     theSeparateCornersAndMedium - if \c true, in quadratic mesh puts 
7229  *         corner and medium nodes in separate groups
7230  */
7231 //================================================================================
7232
7233 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet &   theNodes,
7234                                             const double         theTolerance,
7235                                             TListOfListOfNodes & theGroupsOfNodes,
7236                                             bool                 theSeparateCornersAndMedium)
7237 {
7238   myLastCreatedElems.Clear();
7239   myLastCreatedNodes.Clear();
7240
7241   if ( myMesh->NbEdges  ( ORDER_QUADRATIC ) +
7242        myMesh->NbFaces  ( ORDER_QUADRATIC ) +
7243        myMesh->NbVolumes( ORDER_QUADRATIC ) == 0 )
7244     theSeparateCornersAndMedium = false;
7245
7246   TIDSortedNodeSet& corners = theNodes;
7247   TIDSortedNodeSet  medium;
7248
7249   if ( theNodes.empty() ) // get all nodes in the mesh
7250   {
7251     TIDSortedNodeSet* nodes[2] = { &corners, &medium };
7252     SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(/*idInceasingOrder=*/true);
7253     if ( theSeparateCornersAndMedium )
7254       while ( nIt->more() )
7255       {
7256         const SMDS_MeshNode* n = nIt->next();
7257         TIDSortedNodeSet* & nodeSet = nodes[ SMESH_MesherHelper::IsMedium( n )];
7258         nodeSet->insert( nodeSet->end(), n );
7259       }
7260     else
7261       while ( nIt->more() )
7262         theNodes.insert( theNodes.end(), nIt->next() );
7263   }
7264   else if ( theSeparateCornersAndMedium ) // separate corners from medium nodes
7265   {
7266     TIDSortedNodeSet::iterator nIt = corners.begin();
7267     while ( nIt != corners.end() )
7268       if ( SMESH_MesherHelper::IsMedium( *nIt ))
7269       {
7270         medium.insert( medium.end(), *nIt );
7271         corners.erase( nIt++ );
7272       }
7273       else
7274       {
7275         ++nIt;
7276       }
7277   }
7278
7279   if ( !corners.empty() )
7280     SMESH_OctreeNode::FindCoincidentNodes ( corners, &theGroupsOfNodes, theTolerance );
7281   if ( !medium.empty() )
7282     SMESH_OctreeNode::FindCoincidentNodes ( medium, &theGroupsOfNodes, theTolerance );
7283 }
7284
7285 //=======================================================================
7286 //function : SimplifyFace
7287 //purpose  : split a chain of nodes into several closed chains
7288 //=======================================================================
7289
7290 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
7291                                     vector<const SMDS_MeshNode *>&       poly_nodes,
7292                                     vector<int>&                         quantities) const
7293 {
7294   int nbNodes = faceNodes.size();
7295   while ( faceNodes[ 0 ] == faceNodes[ nbNodes-1 ] && nbNodes > 2 )
7296     --nbNodes;
7297   if ( nbNodes < 3 )
7298     return 0;
7299   size_t prevNbQuant = quantities.size();
7300
7301   vector< const SMDS_MeshNode* > simpleNodes; simpleNodes.reserve( nbNodes );
7302   map< const SMDS_MeshNode*, int > nodeIndices; // indices within simpleNodes
7303   map< const SMDS_MeshNode*, int >::iterator nInd;
7304
7305   nodeIndices.insert( make_pair( faceNodes[0], 0 ));
7306   simpleNodes.push_back( faceNodes[0] );
7307   for ( int iCur = 1; iCur < nbNodes; iCur++ )
7308   {
7309     if ( faceNodes[ iCur ] != simpleNodes.back() )
7310     {
7311       int index = simpleNodes.size();
7312       nInd = nodeIndices.insert( make_pair( faceNodes[ iCur ], index )).first;
7313       int prevIndex = nInd->second;
7314       if ( prevIndex < index )
7315       {
7316         // a sub-loop found
7317         int loopLen = index - prevIndex;
7318         if ( loopLen > 2 )
7319         {
7320           // store the sub-loop
7321           quantities.push_back( loopLen );
7322           for ( int i = prevIndex; i < index; i++ )
7323             poly_nodes.push_back( simpleNodes[ i ]);
7324         }
7325         simpleNodes.resize( prevIndex+1 );
7326       }
7327       else
7328       {
7329         simpleNodes.push_back( faceNodes[ iCur ]);
7330       }
7331     }
7332   }
7333
7334   if ( simpleNodes.size() > 2 )
7335   {
7336     quantities.push_back( simpleNodes.size() );
7337     poly_nodes.insert ( poly_nodes.end(), simpleNodes.begin(), simpleNodes.end() );
7338   }
7339
7340   return quantities.size() - prevNbQuant;
7341 }
7342
7343 //=======================================================================
7344 //function : MergeNodes
7345 //purpose  : In each group, the cdr of nodes are substituted by the first one
7346 //           in all elements.
7347 //=======================================================================
7348
7349 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes,
7350                                    const bool           theAvoidMakingHoles)
7351 {
7352   myLastCreatedElems.Clear();
7353   myLastCreatedNodes.Clear();
7354
7355   SMESHDS_Mesh* mesh = GetMeshDS();
7356
7357   TNodeNodeMap nodeNodeMap; // node to replace - new node
7358   set<const SMDS_MeshElement*> elems; // all elements with changed nodes
7359   list< int > rmElemIds, rmNodeIds;
7360   vector< ElemFeatures > newElemDefs;
7361
7362   // Fill nodeNodeMap and elems
7363
7364   TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
7365   for ( ; grIt != theGroupsOfNodes.end(); grIt++ )
7366   {
7367     list<const SMDS_MeshNode*>& nodes = *grIt;
7368     list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
7369     const SMDS_MeshNode* nToKeep = *nIt;
7370     for ( ++nIt; nIt != nodes.end(); nIt++ )
7371     {
7372       const SMDS_MeshNode* nToRemove = *nIt;
7373       nodeNodeMap.insert( make_pair( nToRemove, nToKeep ));
7374       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
7375       while ( invElemIt->more() ) {
7376         const SMDS_MeshElement* elem = invElemIt->next();
7377         elems.insert(elem);
7378       }
7379     }
7380   }
7381
7382   // Apply recursive replacements (BUG 0020185)
7383   TNodeNodeMap::iterator nnIt = nodeNodeMap.begin();
7384   for ( ; nnIt != nodeNodeMap.end(); ++nnIt )
7385   {
7386     const SMDS_MeshNode* nToKeep = nnIt->second;
7387     TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( nToKeep );
7388     while ( nnIt_i != nodeNodeMap.end() && nnIt_i->second != nnIt->second )
7389       nToKeep = nnIt_i->second;
7390     nnIt->second = nToKeep;
7391   }
7392
7393   if ( theAvoidMakingHoles )
7394   {
7395     // find elements whose topology changes
7396
7397     vector<const SMDS_MeshElement*> pbElems;
7398     set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7399     for ( ; eIt != elems.end(); ++eIt )
7400     {
7401       const SMDS_MeshElement* elem = *eIt;
7402       SMDS_ElemIteratorPtr     itN = elem->nodesIterator();
7403       while ( itN->more() )
7404       {
7405         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7406         TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7407         if ( nnIt != nodeNodeMap.end() && elem->GetNodeIndex( nnIt->second ) >= 0 )
7408         {
7409           // several nodes of elem stick
7410           pbElems.push_back( elem );
7411           break;
7412         }
7413       }
7414     }
7415     // exclude from merge nodes causing spoiling element
7416     for ( size_t iLoop = 0; iLoop < pbElems.size(); ++iLoop ) // avoid infinite cycle
7417     {
7418       bool nodesExcluded = false;
7419       for ( size_t i = 0; i < pbElems.size(); ++i )
7420       {
7421         size_t prevNbMergeNodes = nodeNodeMap.size();
7422         if ( !applyMerge( pbElems[i], newElemDefs, nodeNodeMap, /*noHoles=*/true ) &&
7423              prevNbMergeNodes < nodeNodeMap.size() )
7424           nodesExcluded = true;
7425       }
7426       if ( !nodesExcluded )
7427         break;
7428     }
7429   }
7430
7431   for ( nnIt = nodeNodeMap.begin(); nnIt != nodeNodeMap.end(); ++nnIt )
7432   {
7433     const SMDS_MeshNode* nToRemove = nnIt->first;
7434     const SMDS_MeshNode* nToKeep   = nnIt->second;
7435     if ( nToRemove != nToKeep )
7436     {
7437       rmNodeIds.push_back( nToRemove->GetID() );
7438       AddToSameGroups( nToKeep, nToRemove, mesh );
7439       // set _alwaysComputed to a sub-mesh of VERTEX to enable further mesh computing
7440       // w/o creating node in place of merged ones.
7441       const SMDS_PositionPtr& pos = nToRemove->GetPosition();
7442       if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
7443         if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
7444           sm->SetIsAlwaysComputed( true );
7445     }
7446   }
7447
7448   // Change element nodes or remove an element
7449
7450   set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7451   for ( ; eIt != elems.end(); eIt++ )
7452   {
7453     const SMDS_MeshElement* elem = *eIt;
7454     SMESHDS_SubMesh*          sm = mesh->MeshElements( elem->getshapeId() );
7455
7456     bool keepElem = applyMerge( elem, newElemDefs, nodeNodeMap, /*noHoles=*/false );
7457     if ( !keepElem )
7458       rmElemIds.push_back( elem->GetID() );
7459
7460     for ( size_t i = 0; i < newElemDefs.size(); ++i )
7461     {
7462       if ( i > 0 || !mesh->ChangeElementNodes( elem,
7463                                                & newElemDefs[i].myNodes[0],
7464                                                newElemDefs[i].myNodes.size() ))
7465       {
7466         if ( i == 0 )
7467         {
7468           newElemDefs[i].SetID( elem->GetID() );
7469           mesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
7470           if ( !keepElem ) rmElemIds.pop_back();
7471         }
7472         else
7473         {
7474           newElemDefs[i].SetID( -1 );
7475         }
7476         SMDS_MeshElement* newElem = this->AddElement( newElemDefs[i].myNodes, newElemDefs[i] );
7477         if ( sm && newElem )
7478           sm->AddElement( newElem );
7479         if ( elem != newElem )
7480           ReplaceElemInGroups( elem, newElem, mesh );
7481       }
7482     }
7483   }
7484
7485   // Remove bad elements, then equal nodes (order important)
7486   Remove( rmElemIds, /*isNodes=*/false );
7487   Remove( rmNodeIds, /*isNodes=*/true );
7488
7489   return;
7490 }
7491
7492 //=======================================================================
7493 //function : applyMerge
7494 //purpose  : Compute new connectivity of an element after merging nodes
7495 //  \param [in] elems - the element
7496 //  \param [out] newElemDefs - definition(s) of result element(s)
7497 //  \param [inout] nodeNodeMap - nodes to merge
7498 //  \param [in] avoidMakingHoles - if true and and the element becomes invalid
7499 //              after merging (but not degenerated), removes nodes causing
7500 //              the invalidity from \a nodeNodeMap.
7501 //  \return bool - true if the element should be removed
7502 //=======================================================================
7503
7504 bool SMESH_MeshEditor::applyMerge( const SMDS_MeshElement* elem,
7505                                    vector< ElemFeatures >& newElemDefs,
7506                                    TNodeNodeMap&           nodeNodeMap,
7507                                    const bool              avoidMakingHoles )
7508 {
7509   bool toRemove = false; // to remove elem
7510   int nbResElems = 1;    // nb new elements
7511
7512   newElemDefs.resize(nbResElems);
7513   newElemDefs[0].Init( elem );
7514   newElemDefs[0].myNodes.clear();
7515
7516   set<const SMDS_MeshNode*> nodeSet;
7517   vector< const SMDS_MeshNode*>   curNodes;
7518   vector< const SMDS_MeshNode*> & uniqueNodes = newElemDefs[0].myNodes;
7519   vector<int> iRepl;
7520
7521   const        int  nbNodes = elem->NbNodes();
7522   SMDSAbs_EntityType entity = elem->GetEntityType();
7523
7524   curNodes.resize( nbNodes );
7525   uniqueNodes.resize( nbNodes );
7526   iRepl.resize( nbNodes );
7527   int iUnique = 0, iCur = 0, nbRepl = 0;
7528
7529   // Get new seq of nodes
7530
7531   SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7532   while ( itN->more() )
7533   {
7534     const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7535
7536     TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7537     if ( nnIt != nodeNodeMap.end() ) {
7538       n = (*nnIt).second;
7539     }
7540     curNodes[ iCur ] = n;
7541     bool isUnique = nodeSet.insert( n ).second;
7542     if ( isUnique )
7543       uniqueNodes[ iUnique++ ] = n;
7544     else
7545       iRepl[ nbRepl++ ] = iCur;
7546     iCur++;
7547   }
7548
7549   // Analyse element topology after replacement
7550
7551   int nbUniqueNodes = nodeSet.size();
7552   if ( nbNodes != nbUniqueNodes ) // some nodes stick
7553   {
7554     toRemove = true;
7555     nbResElems = 0;
7556
7557     if ( newElemDefs[0].myIsQuad && newElemDefs[0].myType == SMDSAbs_Face && nbNodes > 6 )
7558     {
7559       // if corner nodes stick, remove medium nodes between them from uniqueNodes
7560       int nbCorners = nbNodes / 2;
7561       for ( int iCur = 0; iCur < nbCorners; ++iCur )
7562       {
7563         int iNext = ( iCur + 1 ) % nbCorners;
7564         if ( curNodes[ iCur ] == curNodes[ iNext ] ) // corners stick
7565         {
7566           int iMedium = iCur + nbCorners;
7567           vector< const SMDS_MeshNode* >::iterator i =
7568             std::find( uniqueNodes.begin() + nbCorners - nbRepl,
7569                        uniqueNodes.end(),
7570                        curNodes[ iMedium ]);
7571           if ( i != uniqueNodes.end() )
7572           {
7573             --nbUniqueNodes;
7574             for ( ; i+1 != uniqueNodes.end(); ++i )
7575               *i = *(i+1);
7576           }
7577         }
7578       }
7579     }
7580
7581     switch ( entity )
7582     {
7583     case SMDSEntity_Polygon:
7584     case SMDSEntity_Quad_Polygon: // Polygon
7585     {
7586       ElemFeatures* elemType = & newElemDefs[0];
7587       const bool isQuad = elemType->myIsQuad;
7588       if ( isQuad )
7589         SMDS_MeshCell::applyInterlace // interlace medium and corner nodes
7590           ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon, nbNodes ), curNodes );
7591
7592       // a polygon can divide into several elements
7593       vector<const SMDS_MeshNode *> polygons_nodes;
7594       vector<int> quantities;
7595       nbResElems = SimplifyFace( curNodes, polygons_nodes, quantities );
7596       newElemDefs.resize( nbResElems );
7597       for ( int inode = 0, iface = 0; iface < nbResElems; iface++ )
7598       {
7599         ElemFeatures* elemType = & newElemDefs[iface];
7600         if ( iface ) elemType->Init( elem );
7601
7602         vector<const SMDS_MeshNode *>& face_nodes = elemType->myNodes;
7603         int nbNewNodes = quantities[iface];
7604         face_nodes.assign( polygons_nodes.begin() + inode,
7605                            polygons_nodes.begin() + inode + nbNewNodes );
7606         inode += nbNewNodes;
7607         if ( isQuad ) // check if a result elem is a valid quadratic polygon
7608         {
7609           bool isValid = ( nbNewNodes % 2 == 0 );
7610           for ( int i = 0; i < nbNewNodes && isValid; ++i )
7611             isValid = ( elem->IsMediumNode( face_nodes[i]) == bool( i % 2 ));
7612           elemType->SetQuad( isValid );
7613           if ( isValid ) // put medium nodes after corners
7614             SMDS_MeshCell::applyInterlaceRev
7615               ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon,
7616                                                     nbNewNodes ), face_nodes );
7617         }
7618         elemType->SetPoly(( nbNewNodes / ( elemType->myIsQuad + 1 ) > 4 ));
7619       }
7620       nbUniqueNodes = newElemDefs[0].myNodes.size();
7621       break;
7622     } // Polygon
7623
7624     case SMDSEntity_Polyhedra: // Polyhedral volume
7625     {
7626       if ( nbUniqueNodes >= 4 )
7627       {
7628         // each face has to be analyzed in order to check volume validity
7629         if ( const SMDS_VtkVolume* aPolyedre = dynamic_cast<const SMDS_VtkVolume*>( elem ))
7630         {
7631           int nbFaces = aPolyedre->NbFaces();
7632
7633           vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7634           vector<int>                  & quantities = newElemDefs[0].myPolyhedQuantities;
7635           vector<const SMDS_MeshNode *>  faceNodes;
7636           poly_nodes.clear();
7637           quantities.clear();
7638
7639           for (int iface = 1; iface <= nbFaces; iface++)
7640           {
7641             int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7642             faceNodes.resize( nbFaceNodes );
7643             for (int inode = 1; inode <= nbFaceNodes; inode++)
7644             {
7645               const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7646               TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7647               if ( nnIt != nodeNodeMap.end() ) // faceNode sticks
7648                 faceNode = (*nnIt).second;
7649               faceNodes[inode - 1] = faceNode;
7650             }
7651             SimplifyFace(faceNodes, poly_nodes, quantities);
7652           }
7653
7654           if ( quantities.size() > 3 )
7655           {
7656             // TODO: remove coincident faces
7657             nbResElems = 1;
7658             nbUniqueNodes = newElemDefs[0].myNodes.size();
7659           }
7660         }
7661       }
7662     }
7663     break;
7664
7665     // Regular elements
7666     // TODO not all the possible cases are solved. Find something more generic?
7667     case SMDSEntity_Edge: //////// EDGE
7668     case SMDSEntity_Triangle: //// TRIANGLE
7669     case SMDSEntity_Quad_Triangle:
7670     case SMDSEntity_Tetra:
7671     case SMDSEntity_Quad_Tetra: // TETRAHEDRON
7672     {
7673       break;
7674     }
7675     case SMDSEntity_Quad_Edge:
7676     {
7677       break;
7678     }
7679     case SMDSEntity_Quadrangle: //////////////////////////////////// QUADRANGLE
7680     {
7681       if ( nbUniqueNodes < 3 )
7682         toRemove = true;
7683       else if ( nbRepl == 1 && curNodes[ iRepl[0]] == curNodes[( iRepl[0]+2 )%4 ])
7684         toRemove = true; // opposite nodes stick
7685       else
7686         toRemove = false;
7687       break;
7688     }
7689     case SMDSEntity_Quad_Quadrangle: // Quadratic QUADRANGLE
7690     {
7691       //   1    5    2
7692       //    +---+---+
7693       //    |       |
7694       //   4+       +6
7695       //    |       |
7696       //    +---+---+
7697       //   0    7    3
7698       if ( nbUniqueNodes == 6 &&
7699            iRepl[0] < 4       &&
7700            ( nbRepl == 1 || iRepl[1] >= 4 ))
7701       {
7702         toRemove = false;
7703       }
7704       break;
7705     }
7706     case SMDSEntity_BiQuad_Quadrangle: // Bi-Quadratic QUADRANGLE
7707     {
7708       //   1    5    2
7709       //    +---+---+
7710       //    |       |
7711       //   4+  8+   +6
7712       //    |       |
7713       //    +---+---+
7714       //   0    7    3
7715       if ( nbUniqueNodes == 7 &&
7716            iRepl[0] < 4       &&
7717            ( nbRepl == 1 || iRepl[1] != 8 ))
7718       {
7719         toRemove = false;
7720       }
7721       break;
7722     }
7723     case SMDSEntity_Penta: ///////////////////////////////////// PENTAHEDRON
7724     {
7725       if ( nbUniqueNodes == 4 ) {
7726         // ---------------------------------> tetrahedron
7727         if ( curNodes[3] == curNodes[4] &&
7728              curNodes[3] == curNodes[5] ) {
7729           // top nodes stick
7730           toRemove = false;
7731         }
7732         else if ( curNodes[0] == curNodes[1] &&
7733                   curNodes[0] == curNodes[2] ) {
7734           // bottom nodes stick: set a top before
7735           uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7736           uniqueNodes[ 0 ] = curNodes [ 5 ];
7737           uniqueNodes[ 1 ] = curNodes [ 4 ];
7738           uniqueNodes[ 2 ] = curNodes [ 3 ];
7739           toRemove = false;
7740         }
7741         else if (( curNodes[0] == curNodes[3] ) +
7742                  ( curNodes[1] == curNodes[4] ) +
7743                  ( curNodes[2] == curNodes[5] ) == 2 ) {
7744           // a lateral face turns into a line
7745           toRemove = false;
7746         }
7747       }
7748       else if ( nbUniqueNodes == 5 ) {
7749         // PENTAHEDRON --------------------> pyramid
7750         if ( curNodes[0] == curNodes[3] )
7751         {
7752           uniqueNodes[ 0 ] = curNodes[ 1 ];
7753           uniqueNodes[ 1 ] = curNodes[ 4 ];
7754           uniqueNodes[ 2 ] = curNodes[ 5 ];
7755           uniqueNodes[ 3 ] = curNodes[ 2 ];
7756           uniqueNodes[ 4 ] = curNodes[ 0 ];
7757           toRemove = false;
7758         }
7759         if ( curNodes[1] == curNodes[4] )
7760         {
7761           uniqueNodes[ 0 ] = curNodes[ 0 ];
7762           uniqueNodes[ 1 ] = curNodes[ 2 ];
7763           uniqueNodes[ 2 ] = curNodes[ 5 ];
7764           uniqueNodes[ 3 ] = curNodes[ 3 ];
7765           uniqueNodes[ 4 ] = curNodes[ 1 ];
7766           toRemove = false;
7767         }
7768         if ( curNodes[2] == curNodes[5] )
7769         {
7770           uniqueNodes[ 0 ] = curNodes[ 0 ];
7771           uniqueNodes[ 1 ] = curNodes[ 3 ];
7772           uniqueNodes[ 2 ] = curNodes[ 4 ];
7773           uniqueNodes[ 3 ] = curNodes[ 1 ];
7774           uniqueNodes[ 4 ] = curNodes[ 2 ];
7775           toRemove = false;
7776         }
7777       }
7778       break;
7779     }
7780     case SMDSEntity_Hexa:
7781     {
7782       //////////////////////////////////// HEXAHEDRON
7783       SMDS_VolumeTool hexa (elem);
7784       hexa.SetExternalNormal();
7785       if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7786         //////////////////////// HEX ---> tetrahedron
7787         for ( int iFace = 0; iFace < 6; iFace++ ) {
7788           const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7789           if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7790               curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7791               curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7792             // one face turns into a point ...
7793             int  pickInd = ind[ 0 ];
7794             int iOppFace = hexa.GetOppFaceIndex( iFace );
7795             ind = hexa.GetFaceNodesIndices( iOppFace );
7796             int nbStick = 0;
7797             uniqueNodes.clear();
7798             for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7799               if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7800                 nbStick++;
7801               else
7802                 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7803             }
7804             if ( nbStick == 1 ) {
7805               // ... and the opposite one - into a triangle.
7806               // set a top node
7807               uniqueNodes.push_back( curNodes[ pickInd ]);
7808               toRemove = false;
7809             }
7810             break;
7811           }
7812         }
7813       }
7814       else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7815         //////////////////////// HEX ---> prism
7816         int nbTria = 0, iTria[3];
7817         const int *ind; // indices of face nodes
7818         // look for triangular faces
7819         for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7820           ind = hexa.GetFaceNodesIndices( iFace );
7821           TIDSortedNodeSet faceNodes;
7822           for ( iCur = 0; iCur < 4; iCur++ )
7823             faceNodes.insert( curNodes[ind[iCur]] );
7824           if ( faceNodes.size() == 3 )
7825             iTria[ nbTria++ ] = iFace;
7826         }
7827         // check if triangles are opposite
7828         if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7829         {
7830           // set nodes of the bottom triangle
7831           ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7832           vector<int> indB;
7833           for ( iCur = 0; iCur < 4; iCur++ )
7834             if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7835               indB.push_back( ind[iCur] );
7836           if ( !hexa.IsForward() )
7837             std::swap( indB[0], indB[2] );
7838           for ( iCur = 0; iCur < 3; iCur++ )
7839             uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7840           // set nodes of the top triangle
7841           const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7842           for ( iCur = 0; iCur < 3; ++iCur )
7843             for ( int j = 0; j < 4; ++j )
7844               if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7845               {
7846                 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7847                 break;
7848               }
7849           toRemove = false;
7850           break;
7851         }
7852       }
7853       else if (nbUniqueNodes == 5 && nbRepl == 3 ) {
7854         //////////////////// HEXAHEDRON ---> pyramid
7855         for ( int iFace = 0; iFace < 6; iFace++ ) {
7856           const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7857           if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7858               curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7859               curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7860             // one face turns into a point ...
7861             int iOppFace = hexa.GetOppFaceIndex( iFace );
7862             ind = hexa.GetFaceNodesIndices( iOppFace );
7863             uniqueNodes.clear();
7864             for ( iCur = 0; iCur < 4; iCur++ ) {
7865               if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7866                 break;
7867               else
7868                 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7869             }
7870             if ( uniqueNodes.size() == 4 ) {
7871               // ... and the opposite one is a quadrangle
7872               // set a top node
7873               const int* indTop = hexa.GetFaceNodesIndices( iFace );
7874               uniqueNodes.push_back( curNodes[indTop[ 0 ]]);
7875               toRemove = false;
7876             }
7877             break;
7878           }
7879         }
7880       }
7881
7882       if ( toRemove && nbUniqueNodes > 4 ) {
7883         ////////////////// HEXAHEDRON ---> polyhedron
7884         hexa.SetExternalNormal();
7885         vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7886         vector<int>                  & quantities = newElemDefs[0].myPolyhedQuantities;
7887         poly_nodes.reserve( 6 * 4 ); poly_nodes.clear();
7888         quantities.reserve( 6 );     quantities.clear();
7889         for ( int iFace = 0; iFace < 6; iFace++ )
7890         {
7891           const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7892           if ( curNodes[ind[0]] == curNodes[ind[2]] ||
7893                curNodes[ind[1]] == curNodes[ind[3]] )
7894           {
7895             quantities.clear();
7896             break; // opposite nodes stick
7897           }
7898           nodeSet.clear();
7899           for ( iCur = 0; iCur < 4; iCur++ )
7900           {
7901             if ( nodeSet.insert( curNodes[ind[ iCur ]] ).second )
7902               poly_nodes.push_back( curNodes[ind[ iCur ]]);
7903           }
7904           if ( nodeSet.size() < 3 )
7905             poly_nodes.resize( poly_nodes.size() - nodeSet.size() );
7906           else
7907             quantities.push_back( nodeSet.size() );
7908         }
7909         if ( quantities.size() >= 4 )
7910         {
7911           nbResElems = 1;
7912           nbUniqueNodes = poly_nodes.size();
7913           newElemDefs[0].SetPoly(true);
7914         }
7915       }
7916       break;
7917     } // case HEXAHEDRON
7918
7919     default:
7920       toRemove = true;
7921
7922     } // switch ( entity )
7923
7924     if ( toRemove && nbResElems == 0 && avoidMakingHoles )
7925     {
7926       // erase from nodeNodeMap nodes whose merge spoils elem
7927       vector< const SMDS_MeshNode* > noMergeNodes;
7928       SMESH_MeshAlgos::DeMerge( elem, curNodes, noMergeNodes );
7929       for ( size_t i = 0; i < noMergeNodes.size(); ++i )
7930         nodeNodeMap.erase( noMergeNodes[i] );
7931     }
7932     
7933   } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7934
7935   uniqueNodes.resize( nbUniqueNodes );
7936
7937   if ( !toRemove && nbResElems == 0 )
7938     nbResElems = 1;
7939
7940   newElemDefs.resize( nbResElems );
7941
7942   return !toRemove;
7943 }
7944
7945
7946 // ========================================================
7947 // class   : SortableElement
7948 // purpose : allow sorting elements basing on their nodes
7949 // ========================================================
7950 class SortableElement : public set <const SMDS_MeshElement*>
7951 {
7952 public:
7953
7954   SortableElement( const SMDS_MeshElement* theElem )
7955   {
7956     myElem = theElem;
7957     SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
7958     while ( nodeIt->more() )
7959       this->insert( nodeIt->next() );
7960   }
7961
7962   const SMDS_MeshElement* Get() const
7963   { return myElem; }
7964
7965 private:
7966   mutable const SMDS_MeshElement* myElem;
7967 };
7968
7969 //=======================================================================
7970 //function : FindEqualElements
7971 //purpose  : Return list of group of elements built on the same nodes.
7972 //           Search among theElements or in the whole mesh if theElements is empty
7973 //=======================================================================
7974
7975 void SMESH_MeshEditor::FindEqualElements(TIDSortedElemSet &        theElements,
7976                                          TListOfListOfElementsID & theGroupsOfElementsID)
7977 {
7978   myLastCreatedElems.Clear();
7979   myLastCreatedNodes.Clear();
7980
7981   typedef map< SortableElement, int > TMapOfNodeSet;
7982   typedef list<int> TGroupOfElems;
7983
7984   if ( theElements.empty() )
7985   { // get all elements in the mesh
7986     SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator();
7987     while ( eIt->more() )
7988       theElements.insert( theElements.end(), eIt->next() );
7989   }
7990
7991   vector< TGroupOfElems > arrayOfGroups;
7992   TGroupOfElems groupOfElems;
7993   TMapOfNodeSet mapOfNodeSet;
7994
7995   TIDSortedElemSet::iterator elemIt = theElements.begin();
7996   for ( int i = 0; elemIt != theElements.end(); ++elemIt )
7997   {
7998     const SMDS_MeshElement* curElem = *elemIt;
7999     SortableElement SE(curElem);
8000     // check uniqueness
8001     pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i));
8002     if ( !pp.second ) { // one more coincident elem
8003       TMapOfNodeSet::iterator& itSE = pp.first;
8004       int ind = (*itSE).second;
8005       arrayOfGroups[ind].push_back( curElem->GetID() );
8006     }
8007     else {
8008       arrayOfGroups.push_back( groupOfElems );
8009       arrayOfGroups.back().push_back( curElem->GetID() );
8010       i++;
8011     }
8012   }
8013
8014   groupOfElems.clear();
8015   vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
8016   for ( ; groupIt != arrayOfGroups.end(); ++groupIt )
8017   {
8018     if ( groupIt->size() > 1 ) {
8019       //groupOfElems.sort(); -- theElements is sorted already
8020       theGroupsOfElementsID.push_back( groupOfElems );
8021       theGroupsOfElementsID.back().splice( theGroupsOfElementsID.back().end(), *groupIt );
8022     }
8023   }
8024 }
8025
8026 //=======================================================================
8027 //function : MergeElements
8028 //purpose  : In each given group, substitute all elements by the first one.
8029 //=======================================================================
8030
8031 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
8032 {
8033   myLastCreatedElems.Clear();
8034   myLastCreatedNodes.Clear();
8035
8036   typedef list<int> TListOfIDs;
8037   TListOfIDs rmElemIds; // IDs of elems to remove
8038
8039   SMESHDS_Mesh* aMesh = GetMeshDS();
8040
8041   TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
8042   while ( groupsIt != theGroupsOfElementsID.end() ) {
8043     TListOfIDs& aGroupOfElemID = *groupsIt;
8044     aGroupOfElemID.sort();
8045     int elemIDToKeep = aGroupOfElemID.front();
8046     const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
8047     aGroupOfElemID.pop_front();
8048     TListOfIDs::iterator idIt = aGroupOfElemID.begin();
8049     while ( idIt != aGroupOfElemID.end() ) {
8050       int elemIDToRemove = *idIt;
8051       const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
8052       // add the kept element in groups of removed one (PAL15188)
8053       AddToSameGroups( elemToKeep, elemToRemove, aMesh );
8054       rmElemIds.push_back( elemIDToRemove );
8055       ++idIt;
8056     }
8057     ++groupsIt;
8058   }
8059
8060   Remove( rmElemIds, false );
8061 }
8062
8063 //=======================================================================
8064 //function : MergeEqualElements
8065 //purpose  : Remove all but one of elements built on the same nodes.
8066 //=======================================================================
8067
8068 void SMESH_MeshEditor::MergeEqualElements()
8069 {
8070   TIDSortedElemSet aMeshElements; /* empty input ==
8071                                      to merge equal elements in the whole mesh */
8072   TListOfListOfElementsID aGroupsOfElementsID;
8073   FindEqualElements(aMeshElements, aGroupsOfElementsID);
8074   MergeElements(aGroupsOfElementsID);
8075 }
8076
8077 //=======================================================================
8078 //function : findAdjacentFace
8079 //purpose  :
8080 //=======================================================================
8081
8082 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
8083                                                 const SMDS_MeshNode* n2,
8084                                                 const SMDS_MeshElement* elem)
8085 {
8086   TIDSortedElemSet elemSet, avoidSet;
8087   if ( elem )
8088     avoidSet.insert ( elem );
8089   return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
8090 }
8091
8092 //=======================================================================
8093 //function : findSegment
8094 //purpose  : Return a mesh segment by two nodes one of which can be medium
8095 //=======================================================================
8096
8097 static const SMDS_MeshElement* findSegment(const SMDS_MeshNode* n1,
8098                                            const SMDS_MeshNode* n2)
8099 {
8100   SMDS_ElemIteratorPtr it = n1->GetInverseElementIterator( SMDSAbs_Edge );
8101   while ( it->more() )
8102   {
8103     const SMDS_MeshElement* seg = it->next();
8104     if ( seg->GetNodeIndex( n2 ) >= 0 )
8105       return seg;
8106   }
8107   return 0;
8108 }
8109
8110 //=======================================================================
8111 //function : FindFreeBorder
8112 //purpose  :
8113 //=======================================================================
8114
8115 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
8116
8117 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode*             theFirstNode,
8118                                        const SMDS_MeshNode*             theSecondNode,
8119                                        const SMDS_MeshNode*             theLastNode,
8120                                        list< const SMDS_MeshNode* > &   theNodes,
8121                                        list< const SMDS_MeshElement* >& theFaces)
8122 {
8123   if ( !theFirstNode || !theSecondNode )
8124     return false;
8125   // find border face between theFirstNode and theSecondNode
8126   const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
8127   if ( !curElem )
8128     return false;
8129
8130   theFaces.push_back( curElem );
8131   theNodes.push_back( theFirstNode );
8132   theNodes.push_back( theSecondNode );
8133
8134   const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
8135   TIDSortedElemSet foundElems;
8136   bool needTheLast = ( theLastNode != 0 );
8137
8138   while ( nStart != theLastNode ) {
8139     if ( nStart == theFirstNode )
8140       return !needTheLast;
8141
8142     // find all free border faces sharing form nStart
8143
8144     list< const SMDS_MeshElement* > curElemList;
8145     list< const SMDS_MeshNode* >    nStartList;
8146     SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
8147     while ( invElemIt->more() ) {
8148       const SMDS_MeshElement* e = invElemIt->next();
8149       if ( e == curElem || foundElems.insert( e ).second ) {
8150         // get nodes
8151         int iNode = 0, nbNodes = e->NbNodes();
8152         vector<const SMDS_MeshNode*> nodes(nbNodes+1);
8153
8154         if ( e->IsQuadratic() ) {
8155           const SMDS_VtkFace* F =
8156             dynamic_cast<const SMDS_VtkFace*>(e);
8157           if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8158           // use special nodes iterator
8159           SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8160           while( anIter->more() ) {
8161             nodes[ iNode++ ] = cast2Node(anIter->next());
8162           }
8163         }
8164         else {
8165           SMDS_ElemIteratorPtr nIt = e->nodesIterator();
8166           while ( nIt->more() )
8167             nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
8168         }
8169         nodes[ iNode ] = nodes[ 0 ];
8170         // check 2 links
8171         for ( iNode = 0; iNode < nbNodes; iNode++ )
8172           if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
8173                (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
8174               ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
8175           {
8176             nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
8177             curElemList.push_back( e );
8178           }
8179       }
8180     }
8181     // analyse the found
8182
8183     int nbNewBorders = curElemList.size();
8184     if ( nbNewBorders == 0 ) {
8185       // no free border furthermore
8186       return !needTheLast;
8187     }
8188     else if ( nbNewBorders == 1 ) {
8189       // one more element found
8190       nIgnore = nStart;
8191       nStart = nStartList.front();
8192       curElem = curElemList.front();
8193       theFaces.push_back( curElem );
8194       theNodes.push_back( nStart );
8195     }
8196     else {
8197       // several continuations found
8198       list< const SMDS_MeshElement* >::iterator curElemIt;
8199       list< const SMDS_MeshNode* >::iterator nStartIt;
8200       // check if one of them reached the last node
8201       if ( needTheLast ) {
8202         for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8203              curElemIt!= curElemList.end();
8204              curElemIt++, nStartIt++ )
8205           if ( *nStartIt == theLastNode ) {
8206             theFaces.push_back( *curElemIt );
8207             theNodes.push_back( *nStartIt );
8208             return true;
8209           }
8210       }
8211       // find the best free border by the continuations
8212       list<const SMDS_MeshNode*>    contNodes[ 2 ], *cNL;
8213       list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
8214       for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8215            curElemIt!= curElemList.end();
8216            curElemIt++, nStartIt++ )
8217       {
8218         cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
8219         cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
8220         // find one more free border
8221         if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
8222           cNL->clear();
8223           cFL->clear();
8224         }
8225         else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
8226           // choice: clear a worse one
8227           int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
8228           int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
8229           contNodes[ iWorse ].clear();
8230           contFaces[ iWorse ].clear();
8231         }
8232       }
8233       if ( contNodes[0].empty() && contNodes[1].empty() )
8234         return false;
8235
8236       // append the best free border
8237       cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
8238       cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8239       theNodes.pop_back(); // remove nIgnore
8240       theNodes.pop_back(); // remove nStart
8241       theFaces.pop_back(); // remove curElem
8242       list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
8243       list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
8244       for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
8245       for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
8246       return true;
8247
8248     } // several continuations found
8249   } // while ( nStart != theLastNode )
8250
8251   return true;
8252 }
8253
8254 //=======================================================================
8255 //function : CheckFreeBorderNodes
8256 //purpose  : Return true if the tree nodes are on a free border
8257 //=======================================================================
8258
8259 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8260                                             const SMDS_MeshNode* theNode2,
8261                                             const SMDS_MeshNode* theNode3)
8262 {
8263   list< const SMDS_MeshNode* > nodes;
8264   list< const SMDS_MeshElement* > faces;
8265   return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8266 }
8267
8268 //=======================================================================
8269 //function : SewFreeBorder
8270 //purpose  :
8271 //warning  : for border-to-side sewing theSideSecondNode is considered as
8272 //           the last side node and theSideThirdNode is not used
8273 //=======================================================================
8274
8275 SMESH_MeshEditor::Sew_Error
8276 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8277                                  const SMDS_MeshNode* theBordSecondNode,
8278                                  const SMDS_MeshNode* theBordLastNode,
8279                                  const SMDS_MeshNode* theSideFirstNode,
8280                                  const SMDS_MeshNode* theSideSecondNode,
8281                                  const SMDS_MeshNode* theSideThirdNode,
8282                                  const bool           theSideIsFreeBorder,
8283                                  const bool           toCreatePolygons,
8284                                  const bool           toCreatePolyedrs)
8285 {
8286   myLastCreatedElems.Clear();
8287   myLastCreatedNodes.Clear();
8288
8289   Sew_Error aResult = SEW_OK;
8290
8291   // ====================================
8292   //    find side nodes and elements
8293   // ====================================
8294
8295   list< const SMDS_MeshNode* >    nSide[ 2 ];
8296   list< const SMDS_MeshElement* > eSide[ 2 ];
8297   list< const SMDS_MeshNode* >::iterator    nIt[ 2 ];
8298   list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8299
8300   // Free border 1
8301   // --------------
8302   if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8303                       nSide[0], eSide[0])) {
8304     MESSAGE(" Free Border 1 not found " );
8305     aResult = SEW_BORDER1_NOT_FOUND;
8306   }
8307   if (theSideIsFreeBorder) {
8308     // Free border 2
8309     // --------------
8310     if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8311                         nSide[1], eSide[1])) {
8312       MESSAGE(" Free Border 2 not found " );
8313       aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8314     }
8315   }
8316   if ( aResult != SEW_OK )
8317     return aResult;
8318
8319   if (!theSideIsFreeBorder) {
8320     // Side 2
8321     // --------------
8322
8323     // -------------------------------------------------------------------------
8324     // Algo:
8325     // 1. If nodes to merge are not coincident, move nodes of the free border
8326     //    from the coord sys defined by the direction from the first to last
8327     //    nodes of the border to the correspondent sys of the side 2
8328     // 2. On the side 2, find the links most co-directed with the correspondent
8329     //    links of the free border
8330     // -------------------------------------------------------------------------
8331
8332     // 1. Since sewing may break if there are volumes to split on the side 2,
8333     //    we won't move nodes but just compute new coordinates for them
8334     typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8335     TNodeXYZMap nBordXYZ;
8336     list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8337     list< const SMDS_MeshNode* >::iterator nBordIt;
8338
8339     gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8340     gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8341     gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8342     gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8343     double tol2 = 1.e-8;
8344     gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8345     if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8346       // Need node movement.
8347
8348       // find X and Z axes to create trsf
8349       gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8350       gp_Vec X = Zs ^ Zb;
8351       if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8352         // Zb || Zs
8353         X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8354
8355       // coord systems
8356       gp_Ax3 toBordAx( Pb1, Zb, X );
8357       gp_Ax3 fromSideAx( Ps1, Zs, X );
8358       gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8359       // set trsf
8360       gp_Trsf toBordSys, fromSide2Sys;
8361       toBordSys.SetTransformation( toBordAx );
8362       fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8363       fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8364
8365       // move
8366       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8367         const SMDS_MeshNode* n = *nBordIt;
8368         gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8369         toBordSys.Transforms( xyz );
8370         fromSide2Sys.Transforms( xyz );
8371         nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8372       }
8373     }
8374     else {
8375       // just insert nodes XYZ in the nBordXYZ map
8376       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8377         const SMDS_MeshNode* n = *nBordIt;
8378         nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8379       }
8380     }
8381
8382     // 2. On the side 2, find the links most co-directed with the correspondent
8383     //    links of the free border
8384
8385     list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8386     list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8387     sideNodes.push_back( theSideFirstNode );
8388
8389     bool hasVolumes = false;
8390     LinkID_Gen aLinkID_Gen( GetMeshDS() );
8391     set<long> foundSideLinkIDs, checkedLinkIDs;
8392     SMDS_VolumeTool volume;
8393     //const SMDS_MeshNode* faceNodes[ 4 ];
8394
8395     const SMDS_MeshNode*    sideNode;
8396     const SMDS_MeshElement* sideElem  = 0;
8397     const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8398     const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8399     nBordIt = bordNodes.begin();
8400     nBordIt++;
8401     // border node position and border link direction to compare with
8402     gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8403     gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8404     // choose next side node by link direction or by closeness to
8405     // the current border node:
8406     bool searchByDir = ( *nBordIt != theBordLastNode );
8407     do {
8408       // find the next node on the Side 2
8409       sideNode = 0;
8410       double maxDot = -DBL_MAX, minDist = DBL_MAX;
8411       long linkID;
8412       checkedLinkIDs.clear();
8413       gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8414
8415       // loop on inverse elements of current node (prevSideNode) on the Side 2
8416       SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8417       while ( invElemIt->more() )
8418       {
8419         const SMDS_MeshElement* elem = invElemIt->next();
8420         // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8421         int iPrevNode = 0, iNode = 0, nbNodes = elem->NbNodes();
8422         vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8423         bool isVolume = volume.Set( elem );
8424         const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8425         if ( isVolume ) // --volume
8426           hasVolumes = true;
8427         else if ( elem->GetType()==SMDSAbs_Face ) { // --face
8428           // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8429           if(elem->IsQuadratic()) {
8430             const SMDS_VtkFace* F =
8431               dynamic_cast<const SMDS_VtkFace*>(elem);
8432             if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8433             // use special nodes iterator
8434             SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8435             while( anIter->more() ) {
8436               nodes[ iNode ] = cast2Node(anIter->next());
8437               if ( nodes[ iNode++ ] == prevSideNode )
8438                 iPrevNode = iNode - 1;
8439             }
8440           }
8441           else {
8442             SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
8443             while ( nIt->more() ) {
8444               nodes[ iNode ] = cast2Node( nIt->next() );
8445               if ( nodes[ iNode++ ] == prevSideNode )
8446                 iPrevNode = iNode - 1;
8447             }
8448           }
8449           // there are 2 links to check
8450           nbNodes = 2;
8451         }
8452         else // --edge
8453           continue;
8454         // loop on links, to be precise, on the second node of links
8455         for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8456           const SMDS_MeshNode* n = nodes[ iNode ];
8457           if ( isVolume ) {
8458             if ( !volume.IsLinked( n, prevSideNode ))
8459               continue;
8460           }
8461           else {
8462             if ( iNode ) // a node before prevSideNode
8463               n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8464             else         // a node after prevSideNode
8465               n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8466           }
8467           // check if this link was already used
8468           long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8469           bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8470           if (!isJustChecked &&
8471               foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8472           {
8473             // test a link geometrically
8474             gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8475             bool linkIsBetter = false;
8476             double dot = 0.0, dist = 0.0;
8477             if ( searchByDir ) { // choose most co-directed link
8478               dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8479               linkIsBetter = ( dot > maxDot );
8480             }
8481             else { // choose link with the node closest to bordPos
8482               dist = ( nextXYZ - bordPos ).SquareModulus();
8483               linkIsBetter = ( dist < minDist );
8484             }
8485             if ( linkIsBetter ) {
8486               maxDot = dot;
8487               minDist = dist;
8488               linkID = iLink;
8489               sideNode = n;
8490               sideElem = elem;
8491             }
8492           }
8493         }
8494       } // loop on inverse elements of prevSideNode
8495
8496       if ( !sideNode ) {
8497         MESSAGE(" Can't find path by links of the Side 2 ");
8498         return SEW_BAD_SIDE_NODES;
8499       }
8500       sideNodes.push_back( sideNode );
8501       sideElems.push_back( sideElem );
8502       foundSideLinkIDs.insert ( linkID );
8503       prevSideNode = sideNode;
8504
8505       if ( *nBordIt == theBordLastNode )
8506         searchByDir = false;
8507       else {
8508         // find the next border link to compare with
8509         gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8510         searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8511         // move to next border node if sideNode is before forward border node (bordPos)
8512         while ( *nBordIt != theBordLastNode && !searchByDir ) {
8513           prevBordNode = *nBordIt;
8514           nBordIt++;
8515           bordPos = nBordXYZ[ *nBordIt ];
8516           bordDir = bordPos - nBordXYZ[ prevBordNode ];
8517           searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8518         }
8519       }
8520     }
8521     while ( sideNode != theSideSecondNode );
8522
8523     if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8524       MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8525       return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8526     }
8527   } // end nodes search on the side 2
8528
8529   // ============================
8530   // sew the border to the side 2
8531   // ============================
8532
8533   int nbNodes[]  = { (int)nSide[0].size(), (int)nSide[1].size() };
8534   int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8535
8536   bool toMergeConformal = ( nbNodes[0] == nbNodes[1] );
8537   if ( toMergeConformal && toCreatePolygons )
8538   {
8539     // do not merge quadrangles if polygons are OK (IPAL0052824)
8540     eIt[0] = eSide[0].begin();
8541     eIt[1] = eSide[1].begin();
8542     bool allQuads[2] = { true, true };
8543     for ( int iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8544       for ( ; allQuads[iBord] && eIt[iBord] != eSide[iBord].end(); ++eIt[iBord] )
8545         allQuads[iBord] = ( (*eIt[iBord])->NbCornerNodes() == 4 );
8546     }
8547     toMergeConformal = ( !allQuads[0] && !allQuads[1] );
8548   }
8549
8550   TListOfListOfNodes nodeGroupsToMerge;
8551   if (( toMergeConformal ) ||
8552       ( theSideIsFreeBorder && !theSideThirdNode )) {
8553
8554     // all nodes are to be merged
8555
8556     for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8557          nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8558          nIt[0]++, nIt[1]++ )
8559     {
8560       nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8561       nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8562       nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8563     }
8564   }
8565   else {
8566
8567     // insert new nodes into the border and the side to get equal nb of segments
8568
8569     // get normalized parameters of nodes on the borders
8570     vector< double > param[ 2 ];
8571     param[0].resize( maxNbNodes );
8572     param[1].resize( maxNbNodes );
8573     int iNode, iBord;
8574     for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8575       list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8576       list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8577       const SMDS_MeshNode* nPrev = *nIt;
8578       double bordLength = 0;
8579       for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8580         const SMDS_MeshNode* nCur = *nIt;
8581         gp_XYZ segment (nCur->X() - nPrev->X(),
8582                         nCur->Y() - nPrev->Y(),
8583                         nCur->Z() - nPrev->Z());
8584         double segmentLen = segment.Modulus();
8585         bordLength += segmentLen;
8586         param[ iBord ][ iNode ] = bordLength;
8587         nPrev = nCur;
8588       }
8589       // normalize within [0,1]
8590       for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8591         param[ iBord ][ iNode ] /= bordLength;
8592       }
8593     }
8594
8595     // loop on border segments
8596     const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8597     int i[ 2 ] = { 0, 0 };
8598     nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8599     nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8600
8601     TElemOfNodeListMap insertMap;
8602     TElemOfNodeListMap::iterator insertMapIt;
8603     // insertMap is
8604     // key:   elem to insert nodes into
8605     // value: 2 nodes to insert between + nodes to be inserted
8606     do {
8607       bool next[ 2 ] = { false, false };
8608
8609       // find min adjacent segment length after sewing
8610       double nextParam = 10., prevParam = 0;
8611       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8612         if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8613           nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8614         if ( i[ iBord ] > 0 )
8615           prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8616       }
8617       double  minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8618       double  maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8619       double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8620
8621       // choose to insert or to merge nodes
8622       double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8623       if ( Abs( du ) <= minSegLen * 0.2 ) {
8624         // merge
8625         // ------
8626         nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8627         const SMDS_MeshNode* n0 = *nIt[0];
8628         const SMDS_MeshNode* n1 = *nIt[1];
8629         nodeGroupsToMerge.back().push_back( n1 );
8630         nodeGroupsToMerge.back().push_back( n0 );
8631         // position of node of the border changes due to merge
8632         param[ 0 ][ i[0] ] += du;
8633         // move n1 for the sake of elem shape evaluation during insertion.
8634         // n1 will be removed by MergeNodes() anyway
8635         const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8636         next[0] = next[1] = true;
8637       }
8638       else {
8639         // insert
8640         // ------
8641         int intoBord = ( du < 0 ) ? 0 : 1;
8642         const SMDS_MeshElement* elem = *eIt [ intoBord ];
8643         const SMDS_MeshNode*    n1   = nPrev[ intoBord ];
8644         const SMDS_MeshNode*    n2   = *nIt [ intoBord ];
8645         const SMDS_MeshNode*    nIns = *nIt [ 1 - intoBord ];
8646         if ( intoBord == 1 ) {
8647           // move node of the border to be on a link of elem of the side
8648           gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8649           gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8650           double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8651           gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8652           GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8653         }
8654         insertMapIt = insertMap.find( elem );
8655         bool  notFound = ( insertMapIt == insertMap.end() );
8656         bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8657         if ( otherLink ) {
8658           // insert into another link of the same element:
8659           // 1. perform insertion into the other link of the elem
8660           list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8661           const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8662           const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8663           InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8664           // 2. perform insertion into the link of adjacent faces
8665           while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem )) {
8666             InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8667           }
8668           while ( const SMDS_MeshElement* seg = findSegment( n12, n22 )) {
8669             InsertNodesIntoLink( seg, n12, n22, nodeList );
8670           }
8671           if (toCreatePolyedrs) {
8672             // perform insertion into the links of adjacent volumes
8673             UpdateVolumes(n12, n22, nodeList);
8674           }
8675           // 3. find an element appeared on n1 and n2 after the insertion
8676           insertMap.erase( elem );
8677           elem = findAdjacentFace( n1, n2, 0 );
8678         }
8679         if ( notFound || otherLink ) {
8680           // add element and nodes of the side into the insertMap
8681           insertMapIt = insertMap.insert( make_pair( elem, list<const SMDS_MeshNode*>() )).first;
8682           (*insertMapIt).second.push_back( n1 );
8683           (*insertMapIt).second.push_back( n2 );
8684         }
8685         // add node to be inserted into elem
8686         (*insertMapIt).second.push_back( nIns );
8687         next[ 1 - intoBord ] = true;
8688       }
8689
8690       // go to the next segment
8691       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8692         if ( next[ iBord ] ) {
8693           if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8694             eIt[ iBord ]++;
8695           nPrev[ iBord ] = *nIt[ iBord ];
8696           nIt[ iBord ]++; i[ iBord ]++;
8697         }
8698       }
8699     }
8700     while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8701
8702     // perform insertion of nodes into elements
8703
8704     for (insertMapIt = insertMap.begin();
8705          insertMapIt != insertMap.end();
8706          insertMapIt++ )
8707     {
8708       const SMDS_MeshElement* elem = (*insertMapIt).first;
8709       list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8710       const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8711       const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8712
8713       InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8714
8715       while ( const SMDS_MeshElement* seg = findSegment( n1, n2 )) {
8716         InsertNodesIntoLink( seg, n1, n2, nodeList );
8717       }
8718
8719       if ( !theSideIsFreeBorder ) {
8720         // look for and insert nodes into the faces adjacent to elem
8721         while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem )) {
8722           InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8723         }
8724       }
8725       if (toCreatePolyedrs) {
8726         // perform insertion into the links of adjacent volumes
8727         UpdateVolumes(n1, n2, nodeList);
8728       }
8729     }
8730   } // end: insert new nodes
8731
8732   MergeNodes ( nodeGroupsToMerge );
8733
8734
8735   // Remove coincident segments
8736
8737   // get new segments
8738   TIDSortedElemSet segments;
8739   SMESH_SequenceOfElemPtr newFaces;
8740   for ( int i = 1; i <= myLastCreatedElems.Length(); ++i )
8741   {
8742     if ( !myLastCreatedElems(i) ) continue;
8743     if ( myLastCreatedElems(i)->GetType() == SMDSAbs_Edge )
8744       segments.insert( segments.end(), myLastCreatedElems(i) );
8745     else
8746       newFaces.Append( myLastCreatedElems(i) );
8747   }
8748   // get segments adjacent to merged nodes
8749   TListOfListOfNodes::iterator groupIt = nodeGroupsToMerge.begin();
8750   for ( ; groupIt != nodeGroupsToMerge.end(); groupIt++ )
8751   {
8752     const list<const SMDS_MeshNode*>& nodes = *groupIt;
8753     SMDS_ElemIteratorPtr segIt = nodes.front()->GetInverseElementIterator( SMDSAbs_Edge );
8754     while ( segIt->more() )
8755       segments.insert( segIt->next() );
8756   }
8757
8758   // find coincident
8759   TListOfListOfElementsID equalGroups;
8760   if ( !segments.empty() )
8761     FindEqualElements( segments, equalGroups );
8762   if ( !equalGroups.empty() )
8763   {
8764     // remove from segments those that will be removed
8765     TListOfListOfElementsID::iterator itGroups = equalGroups.begin();
8766     for ( ; itGroups != equalGroups.end(); ++itGroups )
8767     {
8768       list< int >& group = *itGroups;
8769       list< int >::iterator id = group.begin();
8770       for ( ++id; id != group.end(); ++id )
8771         if ( const SMDS_MeshElement* seg = GetMeshDS()->FindElement( *id ))
8772           segments.erase( seg );
8773     }
8774     // remove equal segments
8775     MergeElements( equalGroups );
8776
8777     // restore myLastCreatedElems
8778     myLastCreatedElems = newFaces;
8779     TIDSortedElemSet::iterator seg = segments.begin();
8780     for ( ; seg != segments.end(); ++seg )
8781       myLastCreatedElems.Append( *seg );
8782   }
8783
8784   return aResult;
8785 }
8786
8787 //=======================================================================
8788 //function : InsertNodesIntoLink
8789 //purpose  : insert theNodesToInsert into theElement between theBetweenNode1
8790 //           and theBetweenNode2 and split theElement
8791 //=======================================================================
8792
8793 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement*     theElement,
8794                                            const SMDS_MeshNode*        theBetweenNode1,
8795                                            const SMDS_MeshNode*        theBetweenNode2,
8796                                            list<const SMDS_MeshNode*>& theNodesToInsert,
8797                                            const bool                  toCreatePoly)
8798 {
8799   if ( !theElement ) return;
8800
8801   SMESHDS_Mesh *aMesh = GetMeshDS();
8802   vector<const SMDS_MeshElement*> newElems;
8803
8804   if ( theElement->GetType() == SMDSAbs_Edge )
8805   {
8806     theNodesToInsert.push_front( theBetweenNode1 );
8807     theNodesToInsert.push_back ( theBetweenNode2 );
8808     list<const SMDS_MeshNode*>::iterator n = theNodesToInsert.begin();
8809     const SMDS_MeshNode* n1 = *n;
8810     for ( ++n; n != theNodesToInsert.end(); ++n )
8811     {
8812       const SMDS_MeshNode* n2 = *n;
8813       if ( const SMDS_MeshElement* seg = aMesh->FindEdge( n1, n2 ))
8814         AddToSameGroups( seg, theElement, aMesh );
8815       else
8816         newElems.push_back( aMesh->AddEdge ( n1, n2 ));
8817       n1 = n2;
8818     }
8819     theNodesToInsert.pop_front();
8820     theNodesToInsert.pop_back();
8821
8822     if ( theElement->IsQuadratic() ) // add a not split part
8823     {
8824       vector<const SMDS_MeshNode*> nodes( theElement->begin_nodes(),
8825                                           theElement->end_nodes() );
8826       int iOther = 0, nbN = nodes.size();
8827       for ( ; iOther < nbN; ++iOther )
8828         if ( nodes[iOther] != theBetweenNode1 &&
8829              nodes[iOther] != theBetweenNode2 )
8830           break;
8831       if      ( iOther == 0 )
8832       {
8833         if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[0], nodes[1] ))
8834           AddToSameGroups( seg, theElement, aMesh );
8835         else
8836           newElems.push_back( aMesh->AddEdge ( nodes[0], nodes[1] ));
8837       }
8838       else if ( iOther == 2 )
8839       {
8840         if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[1], nodes[2] ))
8841           AddToSameGroups( seg, theElement, aMesh );
8842         else
8843           newElems.push_back( aMesh->AddEdge ( nodes[1], nodes[2] ));
8844       }
8845     }
8846     // treat new elements
8847     for ( size_t i = 0; i < newElems.size(); ++i )
8848       if ( newElems[i] )
8849       {
8850         aMesh->SetMeshElementOnShape( newElems[i], theElement->getshapeId() );
8851         myLastCreatedElems.Append( newElems[i] );
8852       }
8853     ReplaceElemInGroups( theElement, newElems, aMesh );
8854     aMesh->RemoveElement( theElement );
8855     return;
8856
8857   } // if ( theElement->GetType() == SMDSAbs_Edge )
8858
8859   const SMDS_MeshElement* theFace = theElement;
8860   if ( theFace->GetType() != SMDSAbs_Face ) return;
8861
8862   // find indices of 2 link nodes and of the rest nodes
8863   int iNode = 0, il1, il2, i3, i4;
8864   il1 = il2 = i3 = i4 = -1;
8865   vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8866
8867   SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8868   while ( nodeIt->more() ) {
8869     const SMDS_MeshNode* n = nodeIt->next();
8870     if ( n == theBetweenNode1 )
8871       il1 = iNode;
8872     else if ( n == theBetweenNode2 )
8873       il2 = iNode;
8874     else if ( i3 < 0 )
8875       i3 = iNode;
8876     else
8877       i4 = iNode;
8878     nodes[ iNode++ ] = n;
8879   }
8880   if ( il1 < 0 || il2 < 0 || i3 < 0 )
8881     return ;
8882
8883   // arrange link nodes to go one after another regarding the face orientation
8884   bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8885   list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8886   if ( reverse ) {
8887     iNode = il1;
8888     il1 = il2;
8889     il2 = iNode;
8890     aNodesToInsert.reverse();
8891   }
8892   // check that not link nodes of a quadrangles are in good order
8893   int nbFaceNodes = theFace->NbNodes();
8894   if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8895     iNode = i3;
8896     i3 = i4;
8897     i4 = iNode;
8898   }
8899
8900   if (toCreatePoly || theFace->IsPoly()) {
8901
8902     iNode = 0;
8903     vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8904
8905     // add nodes of face up to first node of link
8906     bool isFLN = false;
8907
8908     if ( theFace->IsQuadratic() ) {
8909       const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>(theFace);
8910       if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8911       // use special nodes iterator
8912       SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8913       while( anIter->more()  && !isFLN ) {
8914         const SMDS_MeshNode* n = cast2Node(anIter->next());
8915         poly_nodes[iNode++] = n;
8916         if (n == nodes[il1]) {
8917           isFLN = true;
8918         }
8919       }
8920       // add nodes to insert
8921       list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8922       for (; nIt != aNodesToInsert.end(); nIt++) {
8923         poly_nodes[iNode++] = *nIt;
8924       }
8925       // add nodes of face starting from last node of link
8926       while ( anIter->more() ) {
8927         poly_nodes[iNode++] = cast2Node(anIter->next());
8928       }
8929     }
8930     else {
8931       SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8932       while ( nodeIt->more() && !isFLN ) {
8933         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8934         poly_nodes[iNode++] = n;
8935         if (n == nodes[il1]) {
8936           isFLN = true;
8937         }
8938       }
8939       // add nodes to insert
8940       list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8941       for (; nIt != aNodesToInsert.end(); nIt++) {
8942         poly_nodes[iNode++] = *nIt;
8943       }
8944       // add nodes of face starting from last node of link
8945       while ( nodeIt->more() ) {
8946         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8947         poly_nodes[iNode++] = n;
8948       }
8949     }
8950
8951     // make a new face
8952     newElems.push_back( aMesh->AddPolygonalFace( poly_nodes ));
8953   }
8954
8955   else if ( !theFace->IsQuadratic() )
8956   {
8957     // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8958     int nbLinkNodes = 2 + aNodesToInsert.size();
8959     //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8960     vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8961     linkNodes[ 0 ] = nodes[ il1 ];
8962     linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8963     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8964     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8965       linkNodes[ iNode++ ] = *nIt;
8966     }
8967     // decide how to split a quadrangle: compare possible variants
8968     // and choose which of splits to be a quadrangle
8969     int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad = 0;
8970     if ( nbFaceNodes == 3 ) {
8971       iBestQuad = nbSplits;
8972       i4 = i3;
8973     }
8974     else if ( nbFaceNodes == 4 ) {
8975       SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8976       double aBestRate = DBL_MAX;
8977       for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8978         i1 = 0; i2 = 1;
8979         double aBadRate = 0;
8980         // evaluate elements quality
8981         for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8982           if ( iSplit == iQuad ) {
8983             SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8984                                    linkNodes[ i2++ ],
8985                                    nodes[ i3 ],
8986                                    nodes[ i4 ]);
8987             aBadRate += getBadRate( &quad, aCrit );
8988           }
8989           else {
8990             SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8991                                    linkNodes[ i2++ ],
8992                                    nodes[ iSplit < iQuad ? i4 : i3 ]);
8993             aBadRate += getBadRate( &tria, aCrit );
8994           }
8995         }
8996         // choice
8997         if ( aBadRate < aBestRate ) {
8998           iBestQuad = iQuad;
8999           aBestRate = aBadRate;
9000         }
9001       }
9002     }
9003
9004     // create new elements
9005     i1 = 0; i2 = 1;
9006     for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ )
9007     {
9008       if ( iSplit == iBestQuad )
9009         newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
9010                                             linkNodes[ i2++ ],
9011                                             nodes[ i3 ],
9012                                             nodes[ i4 ]));
9013       else
9014         newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
9015                                             linkNodes[ i2++ ],
9016                                             nodes[ iSplit < iBestQuad ? i4 : i3 ]));
9017     }
9018
9019     const SMDS_MeshNode* newNodes[ 4 ];
9020     newNodes[ 0 ] = linkNodes[ i1 ];
9021     newNodes[ 1 ] = linkNodes[ i2 ];
9022     newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
9023     newNodes[ 3 ] = nodes[ i4 ];
9024     if (iSplit == iBestQuad)
9025       newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] ));
9026     else
9027       newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] ));
9028
9029   } // end if(!theFace->IsQuadratic())
9030
9031   else { // theFace is quadratic
9032     // we have to split theFace on simple triangles and one simple quadrangle
9033     int tmp = il1/2;
9034     int nbshift = tmp*2;
9035     // shift nodes in nodes[] by nbshift
9036     int i,j;
9037     for(i=0; i<nbshift; i++) {
9038       const SMDS_MeshNode* n = nodes[0];
9039       for(j=0; j<nbFaceNodes-1; j++) {
9040         nodes[j] = nodes[j+1];
9041       }
9042       nodes[nbFaceNodes-1] = n;
9043     }
9044     il1 = il1 - nbshift;
9045     // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
9046     //   n0      n1     n2    n0      n1     n2
9047     //     +-----+-----+        +-----+-----+
9048     //      \         /         |           |
9049     //       \       /          |           |
9050     //      n5+     +n3       n7+           +n3
9051     //         \   /            |           |
9052     //          \ /             |           |
9053     //           +              +-----+-----+
9054     //           n4           n6      n5     n4
9055
9056     // create new elements
9057     int n1,n2,n3;
9058     if ( nbFaceNodes == 6 ) { // quadratic triangle
9059       newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
9060       if ( theFace->IsMediumNode(nodes[il1]) ) {
9061         // create quadrangle
9062         newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[5] ));
9063         n1 = 1;
9064         n2 = 2;
9065         n3 = 3;
9066       }
9067       else {
9068         // create quadrangle
9069         newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[5] ));
9070         n1 = 0;
9071         n2 = 1;
9072         n3 = 5;
9073       }
9074     }
9075     else { // nbFaceNodes==8 - quadratic quadrangle
9076       newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
9077       newElems.push_back( aMesh->AddFace( nodes[5], nodes[6], nodes[7] ));
9078       newElems.push_back( aMesh->AddFace( nodes[5], nodes[7], nodes[3] ));
9079       if ( theFace->IsMediumNode( nodes[ il1 ])) {
9080         // create quadrangle
9081         newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[7] ));
9082         n1 = 1;
9083         n2 = 2;
9084         n3 = 3;
9085       }
9086       else {
9087         // create quadrangle
9088         newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[7] ));
9089         n1 = 0;
9090         n2 = 1;
9091         n3 = 7;
9092       }
9093     }
9094     // create needed triangles using n1,n2,n3 and inserted nodes
9095     int nbn = 2 + aNodesToInsert.size();
9096     vector<const SMDS_MeshNode*> aNodes(nbn);
9097     aNodes[0    ] = nodes[n1];
9098     aNodes[nbn-1] = nodes[n2];
9099     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9100     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
9101       aNodes[iNode++] = *nIt;
9102     }
9103     for ( i = 1; i < nbn; i++ )
9104       newElems.push_back( aMesh->AddFace( aNodes[i-1], aNodes[i], nodes[n3] ));
9105   }
9106
9107   // remove the old face
9108   for ( size_t i = 0; i < newElems.size(); ++i )
9109     if ( newElems[i] )
9110     {
9111       aMesh->SetMeshElementOnShape( newElems[i], theFace->getshapeId() );
9112       myLastCreatedElems.Append( newElems[i] );
9113     }
9114   ReplaceElemInGroups( theFace, newElems, aMesh );
9115   aMesh->RemoveElement(theFace);
9116
9117 } // InsertNodesIntoLink()
9118
9119 //=======================================================================
9120 //function : UpdateVolumes
9121 //purpose  :
9122 //=======================================================================
9123
9124 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode*        theBetweenNode1,
9125                                       const SMDS_MeshNode*        theBetweenNode2,
9126                                       list<const SMDS_MeshNode*>& theNodesToInsert)
9127 {
9128   myLastCreatedElems.Clear();
9129   myLastCreatedNodes.Clear();
9130
9131   SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
9132   while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
9133     const SMDS_MeshElement* elem = invElemIt->next();
9134
9135     // check, if current volume has link theBetweenNode1 - theBetweenNode2
9136     SMDS_VolumeTool aVolume (elem);
9137     if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
9138       continue;
9139
9140     // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
9141     int iface, nbFaces = aVolume.NbFaces();
9142     vector<const SMDS_MeshNode *> poly_nodes;
9143     vector<int> quantities (nbFaces);
9144
9145     for (iface = 0; iface < nbFaces; iface++) {
9146       int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
9147       // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
9148       const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
9149
9150       for (int inode = 0; inode < nbFaceNodes; inode++) {
9151         poly_nodes.push_back(faceNodes[inode]);
9152
9153         if (nbInserted == 0) {
9154           if (faceNodes[inode] == theBetweenNode1) {
9155             if (faceNodes[inode + 1] == theBetweenNode2) {
9156               nbInserted = theNodesToInsert.size();
9157
9158               // add nodes to insert
9159               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
9160               for (; nIt != theNodesToInsert.end(); nIt++) {
9161                 poly_nodes.push_back(*nIt);
9162               }
9163             }
9164           }
9165           else if (faceNodes[inode] == theBetweenNode2) {
9166             if (faceNodes[inode + 1] == theBetweenNode1) {
9167               nbInserted = theNodesToInsert.size();
9168
9169               // add nodes to insert in reversed order
9170               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
9171               nIt--;
9172               for (; nIt != theNodesToInsert.begin(); nIt--) {
9173                 poly_nodes.push_back(*nIt);
9174               }
9175               poly_nodes.push_back(*nIt);
9176             }
9177           }
9178           else {
9179           }
9180         }
9181       }
9182       quantities[iface] = nbFaceNodes + nbInserted;
9183     }
9184
9185     // Replace the volume
9186     SMESHDS_Mesh *aMesh = GetMeshDS();
9187
9188     if ( SMDS_MeshElement* newElem = aMesh->AddPolyhedralVolume( poly_nodes, quantities ))
9189     {
9190       aMesh->SetMeshElementOnShape( newElem, elem->getshapeId() );
9191       myLastCreatedElems.Append( newElem );
9192       ReplaceElemInGroups( elem, newElem, aMesh );
9193     }
9194     aMesh->RemoveElement( elem );
9195   }
9196 }
9197
9198 namespace
9199 {
9200   //================================================================================
9201   /*!
9202    * \brief Transform any volume into data of SMDSEntity_Polyhedra
9203    */
9204   //================================================================================
9205
9206   void volumeToPolyhedron( const SMDS_MeshElement*         elem,
9207                            vector<const SMDS_MeshNode *> & nodes,
9208                            vector<int> &                   nbNodeInFaces )
9209   {
9210     nodes.clear();
9211     nbNodeInFaces.clear();
9212     SMDS_VolumeTool vTool ( elem );
9213     for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
9214     {
9215       const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
9216       nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
9217       nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
9218     }
9219   }
9220 }
9221
9222 //=======================================================================
9223 /*!
9224  * \brief Convert elements contained in a sub-mesh to quadratic
9225  * \return int - nb of checked elements
9226  */
9227 //=======================================================================
9228
9229 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh *   theSm,
9230                                              SMESH_MesherHelper& theHelper,
9231                                              const bool          theForce3d)
9232 {
9233   //MESSAGE("convertElemToQuadratic");
9234   int nbElem = 0;
9235   if( !theSm ) return nbElem;
9236
9237   vector<int> nbNodeInFaces;
9238   vector<const SMDS_MeshNode *> nodes;
9239   SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
9240   while(ElemItr->more())
9241   {
9242     nbElem++;
9243     const SMDS_MeshElement* elem = ElemItr->next();
9244     if( !elem ) continue;
9245
9246     // analyse a necessity of conversion
9247     const SMDSAbs_ElementType aType = elem->GetType();
9248     if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
9249       continue;
9250     const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
9251     bool hasCentralNodes = false;
9252     if ( elem->IsQuadratic() )
9253     {
9254       bool alreadyOK;
9255       switch ( aGeomType ) {
9256       case SMDSEntity_Quad_Triangle:
9257       case SMDSEntity_Quad_Quadrangle:
9258       case SMDSEntity_Quad_Hexa:
9259       case SMDSEntity_Quad_Penta:
9260         alreadyOK = !theHelper.GetIsBiQuadratic(); break;
9261
9262       case SMDSEntity_BiQuad_Triangle:
9263       case SMDSEntity_BiQuad_Quadrangle:
9264       case SMDSEntity_TriQuad_Hexa:
9265       case SMDSEntity_BiQuad_Penta:
9266         alreadyOK = theHelper.GetIsBiQuadratic();
9267         hasCentralNodes = true;
9268         break;
9269       default:
9270         alreadyOK = true;
9271       }
9272       // take into account already present medium nodes
9273       switch ( aType ) {
9274       case SMDSAbs_Volume:
9275         theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
9276       case SMDSAbs_Face:
9277         theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
9278       case SMDSAbs_Edge:
9279         theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
9280       default:;
9281       }
9282       if ( alreadyOK )
9283         continue;
9284     }
9285     // get elem data needed to re-create it
9286     //
9287     const int id      = elem->GetID();
9288     const int nbNodes = elem->NbCornerNodes();
9289     nodes.assign(elem->begin_nodes(), elem->end_nodes());
9290     if ( aGeomType == SMDSEntity_Polyhedra )
9291       nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
9292     else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
9293       volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9294
9295     // remove a linear element
9296     GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9297
9298     // remove central nodes of biquadratic elements (biquad->quad conversion)
9299     if ( hasCentralNodes )
9300       for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
9301         if ( nodes[i]->NbInverseElements() == 0 )
9302           GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
9303
9304     const SMDS_MeshElement* NewElem = 0;
9305
9306     switch( aType )
9307     {
9308     case SMDSAbs_Edge :
9309       {
9310         NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9311         break;
9312       }
9313     case SMDSAbs_Face :
9314       {
9315         switch(nbNodes)
9316         {
9317         case 3:
9318           NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9319           break;
9320         case 4:
9321           NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9322           break;
9323         default:
9324           NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9325         }
9326         break;
9327       }
9328     case SMDSAbs_Volume :
9329       {
9330         switch( aGeomType )
9331         {
9332         case SMDSEntity_Tetra:
9333           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9334           break;
9335         case SMDSEntity_Pyramid:
9336           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9337           break;
9338         case SMDSEntity_Penta:
9339         case SMDSEntity_Quad_Penta:
9340         case SMDSEntity_BiQuad_Penta:
9341           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9342           break;
9343         case SMDSEntity_Hexa:
9344         case SMDSEntity_Quad_Hexa:
9345         case SMDSEntity_TriQuad_Hexa:
9346           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9347                                         nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9348           break;
9349         case SMDSEntity_Hexagonal_Prism:
9350         default:
9351           NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9352         }
9353         break;
9354       }
9355     default :
9356       continue;
9357     }
9358     ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9359     if( NewElem && NewElem->getshapeId() < 1 )
9360       theSm->AddElement( NewElem );
9361   }
9362   return nbElem;
9363 }
9364 //=======================================================================
9365 //function : ConvertToQuadratic
9366 //purpose  :
9367 //=======================================================================
9368
9369 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
9370 {
9371   //MESSAGE("ConvertToQuadratic "<< theForce3d << " " << theToBiQuad);
9372   SMESHDS_Mesh* meshDS = GetMeshDS();
9373
9374   SMESH_MesherHelper aHelper(*myMesh);
9375
9376   aHelper.SetIsQuadratic( true );
9377   aHelper.SetIsBiQuadratic( theToBiQuad );
9378   aHelper.SetElementsOnShape(true);
9379   aHelper.ToFixNodeParameters( true );
9380
9381   // convert elements assigned to sub-meshes
9382   int nbCheckedElems = 0;
9383   if ( myMesh->HasShapeToMesh() )
9384   {
9385     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9386     {
9387       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9388       while ( smIt->more() ) {
9389         SMESH_subMesh* sm = smIt->next();
9390         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9391           aHelper.SetSubShape( sm->GetSubShape() );
9392           nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9393         }
9394       }
9395     }
9396   }
9397
9398   // convert elements NOT assigned to sub-meshes
9399   int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9400   if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
9401   {
9402     aHelper.SetElementsOnShape(false);
9403     SMESHDS_SubMesh *smDS = 0;
9404
9405     // convert edges
9406     SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9407     while( aEdgeItr->more() )
9408     {
9409       const SMDS_MeshEdge* edge = aEdgeItr->next();
9410       if ( !edge->IsQuadratic() )
9411       {
9412         int                  id = edge->GetID();
9413         const SMDS_MeshNode* n1 = edge->GetNode(0);
9414         const SMDS_MeshNode* n2 = edge->GetNode(1);
9415
9416         meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9417
9418         const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9419         ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9420       }
9421       else
9422       {
9423         aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
9424       }
9425     }
9426
9427     // convert faces
9428     SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9429     while( aFaceItr->more() )
9430     {
9431       const SMDS_MeshFace* face = aFaceItr->next();
9432       if ( !face ) continue;
9433       
9434       const SMDSAbs_EntityType type = face->GetEntityType();
9435       bool alreadyOK;
9436       switch( type )
9437       {
9438       case SMDSEntity_Quad_Triangle:
9439       case SMDSEntity_Quad_Quadrangle:
9440         alreadyOK = !theToBiQuad;
9441         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9442         break;
9443       case SMDSEntity_BiQuad_Triangle:
9444       case SMDSEntity_BiQuad_Quadrangle:
9445         alreadyOK = theToBiQuad;
9446         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9447         break;
9448       default: alreadyOK = false;
9449       }
9450       if ( alreadyOK )
9451         continue;
9452
9453       const int id = face->GetID();
9454       vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9455
9456       meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9457
9458       SMDS_MeshFace * NewFace = 0;
9459       switch( type )
9460       {
9461       case SMDSEntity_Triangle:
9462       case SMDSEntity_Quad_Triangle:
9463       case SMDSEntity_BiQuad_Triangle:
9464         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9465         if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
9466           GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
9467         break;
9468
9469       case SMDSEntity_Quadrangle:
9470       case SMDSEntity_Quad_Quadrangle:
9471       case SMDSEntity_BiQuad_Quadrangle:
9472         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9473         if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
9474           GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
9475         break;
9476
9477       default:;
9478         NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9479       }
9480       ReplaceElemInGroups( face, NewFace, GetMeshDS());
9481     }
9482
9483     // convert volumes
9484     vector<int> nbNodeInFaces;
9485     SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9486     while(aVolumeItr->more())
9487     {
9488       const SMDS_MeshVolume* volume = aVolumeItr->next();
9489       if ( !volume ) continue;
9490
9491       const SMDSAbs_EntityType type = volume->GetEntityType();
9492       if ( volume->IsQuadratic() )
9493       {
9494         bool alreadyOK;
9495         switch ( type )
9496         {
9497         case SMDSEntity_Quad_Hexa:    alreadyOK = !theToBiQuad; break;
9498         case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9499         case SMDSEntity_Quad_Penta:   alreadyOK = !theToBiQuad; break;
9500         case SMDSEntity_BiQuad_Penta: alreadyOK = theToBiQuad; break;
9501         default:                      alreadyOK = true;
9502         }
9503         if ( alreadyOK )
9504         {
9505           aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
9506           continue;
9507         }
9508       }
9509       const int id = volume->GetID();
9510       vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9511       if ( type == SMDSEntity_Polyhedra )
9512         nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
9513       else if ( type == SMDSEntity_Hexagonal_Prism )
9514         volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9515
9516       meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9517
9518       SMDS_MeshVolume * NewVolume = 0;
9519       switch ( type )
9520       {
9521       case SMDSEntity_Tetra:
9522         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9523         break;
9524       case SMDSEntity_Hexa:
9525       case SMDSEntity_Quad_Hexa:
9526       case SMDSEntity_TriQuad_Hexa:
9527         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9528                                       nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9529         for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
9530           if ( nodes[i]->NbInverseElements() == 0 )
9531             GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9532         break;
9533       case SMDSEntity_Pyramid:
9534         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9535                                       nodes[3], nodes[4], id, theForce3d);
9536         break;
9537       case SMDSEntity_Penta:
9538       case SMDSEntity_Quad_Penta:
9539       case SMDSEntity_BiQuad_Penta:
9540         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9541                                       nodes[3], nodes[4], nodes[5], id, theForce3d);
9542         for ( size_t i = 15; i < nodes.size(); ++i ) // rm central nodes
9543           if ( nodes[i]->NbInverseElements() == 0 )
9544             GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9545         break;
9546       case SMDSEntity_Hexagonal_Prism:
9547       default:
9548         NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9549       }
9550       ReplaceElemInGroups(volume, NewVolume, meshDS);
9551     }
9552   }
9553
9554   if ( !theForce3d )
9555   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9556     // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9557     // aHelper.FixQuadraticElements(myError);
9558     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9559   }
9560 }
9561
9562 //================================================================================
9563 /*!
9564  * \brief Makes given elements quadratic
9565  *  \param theForce3d - if true, the medium nodes will be placed in the middle of link
9566  *  \param theElements - elements to make quadratic
9567  */
9568 //================================================================================
9569
9570 void SMESH_MeshEditor::ConvertToQuadratic(const bool        theForce3d,
9571                                           TIDSortedElemSet& theElements,
9572                                           const bool        theToBiQuad)
9573 {
9574   if ( theElements.empty() ) return;
9575
9576   // we believe that all theElements are of the same type
9577   const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9578
9579   // get all nodes shared by theElements
9580   TIDSortedNodeSet allNodes;
9581   TIDSortedElemSet::iterator eIt = theElements.begin();
9582   for ( ; eIt != theElements.end(); ++eIt )
9583     allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9584
9585   // complete theElements with elements of lower dim whose all nodes are in allNodes
9586
9587   TIDSortedElemSet quadAdjacentElems    [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9588   TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9589   TIDSortedNodeSet::iterator nIt = allNodes.begin();
9590   for ( ; nIt != allNodes.end(); ++nIt )
9591   {
9592     const SMDS_MeshNode* n = *nIt;
9593     SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9594     while ( invIt->more() )
9595     {
9596       const SMDS_MeshElement*      e = invIt->next();
9597       const SMDSAbs_ElementType type = e->GetType();
9598       if ( e->IsQuadratic() )
9599       {
9600         quadAdjacentElems[ type ].insert( e );
9601
9602         bool alreadyOK;
9603         switch ( e->GetEntityType() ) {
9604         case SMDSEntity_Quad_Triangle:
9605         case SMDSEntity_Quad_Quadrangle:
9606         case SMDSEntity_Quad_Hexa:         alreadyOK = !theToBiQuad; break;
9607         case SMDSEntity_BiQuad_Triangle:
9608         case SMDSEntity_BiQuad_Quadrangle:
9609         case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; break;
9610         default:                           alreadyOK = true;
9611         }
9612         if ( alreadyOK )
9613           continue;
9614       }
9615       if ( type >= elemType )
9616         continue; // same type or more complex linear element
9617
9618       if ( !checkedAdjacentElems[ type ].insert( e ).second )
9619         continue; // e is already checked
9620
9621       // check nodes
9622       bool allIn = true;
9623       SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9624       while ( nodeIt->more() && allIn )
9625         allIn = allNodes.count( nodeIt->next() );
9626       if ( allIn )
9627         theElements.insert(e );
9628     }
9629   }
9630
9631   SMESH_MesherHelper helper(*myMesh);
9632   helper.SetIsQuadratic( true );
9633   helper.SetIsBiQuadratic( theToBiQuad );
9634
9635   // add links of quadratic adjacent elements to the helper
9636
9637   if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9638     for ( eIt  = quadAdjacentElems[SMDSAbs_Edge].begin();
9639           eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9640     {
9641       helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9642     }
9643   if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9644     for ( eIt  = quadAdjacentElems[SMDSAbs_Face].begin();
9645           eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9646     {
9647       helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9648     }
9649   if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9650     for ( eIt  = quadAdjacentElems[SMDSAbs_Volume].begin();
9651           eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9652     {
9653       helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9654     }
9655
9656   // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9657
9658   SMESHDS_Mesh*  meshDS = GetMeshDS();
9659   SMESHDS_SubMesh* smDS = 0;
9660   for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9661   {
9662     const SMDS_MeshElement* elem = *eIt;
9663
9664     bool alreadyOK;
9665     int nbCentralNodes = 0;
9666     switch ( elem->GetEntityType() ) {
9667       // linear convertible
9668     case SMDSEntity_Edge:
9669     case SMDSEntity_Triangle:
9670     case SMDSEntity_Quadrangle:
9671     case SMDSEntity_Tetra:
9672     case SMDSEntity_Pyramid:
9673     case SMDSEntity_Hexa:
9674     case SMDSEntity_Penta:             alreadyOK = false;       nbCentralNodes = 0; break;
9675       // quadratic that can become bi-quadratic
9676     case SMDSEntity_Quad_Triangle:
9677     case SMDSEntity_Quad_Quadrangle:
9678     case SMDSEntity_Quad_Hexa:         alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9679       // bi-quadratic
9680     case SMDSEntity_BiQuad_Triangle:
9681     case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9682     case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9683       // the rest
9684     default:                           alreadyOK = true;
9685     }
9686     if ( alreadyOK ) continue;
9687
9688     const SMDSAbs_ElementType type = elem->GetType();
9689     const int                   id = elem->GetID();
9690     const int              nbNodes = elem->NbCornerNodes();
9691     vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9692
9693     helper.SetSubShape( elem->getshapeId() );
9694
9695     if ( !smDS || !smDS->Contains( elem ))
9696       smDS = meshDS->MeshElements( elem->getshapeId() );
9697     meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9698
9699     SMDS_MeshElement * newElem = 0;
9700     switch( nbNodes )
9701     {
9702     case 4: // cases for most frequently used element types go first (for optimization)
9703       if ( type == SMDSAbs_Volume )
9704         newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9705       else
9706         newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9707       break;
9708     case 8:
9709       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9710                                  nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9711       break;
9712     case 3:
9713       newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], id, theForce3d);
9714       break;
9715     case 2:
9716       newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9717       break;
9718     case 5:
9719       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9720                                  nodes[4], id, theForce3d);
9721       break;
9722     case 6:
9723       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9724                                  nodes[4], nodes[5], id, theForce3d);
9725       break;
9726     default:;
9727     }
9728     ReplaceElemInGroups( elem, newElem, meshDS);
9729     if( newElem && smDS )
9730       smDS->AddElement( newElem );
9731
9732      // remove central nodes
9733     for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9734       if ( nodes[i]->NbInverseElements() == 0 )
9735         meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9736
9737   } // loop on theElements
9738
9739   if ( !theForce3d )
9740   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9741     // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9742     // helper.FixQuadraticElements( myError );
9743     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9744   }
9745 }
9746
9747 //=======================================================================
9748 /*!
9749  * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9750  * \return int - nb of checked elements
9751  */
9752 //=======================================================================
9753
9754 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh *    theSm,
9755                                      SMDS_ElemIteratorPtr theItr,
9756                                      const int            theShapeID)
9757 {
9758   int nbElem = 0;
9759   SMESHDS_Mesh* meshDS = GetMeshDS();
9760   ElemFeatures elemType;
9761   vector<const SMDS_MeshNode *> nodes;
9762
9763   while( theItr->more() )
9764   {
9765     const SMDS_MeshElement* elem = theItr->next();
9766     nbElem++;
9767     if( elem && elem->IsQuadratic())
9768     {
9769       // get elem data
9770       int nbCornerNodes = elem->NbCornerNodes();
9771       nodes.assign( elem->begin_nodes(), elem->end_nodes() );
9772
9773       elemType.Init( elem, /*basicOnly=*/false ).SetID( elem->GetID() ).SetQuad( false );
9774
9775       //remove a quadratic element
9776       if ( !theSm || !theSm->Contains( elem ))
9777         theSm = meshDS->MeshElements( elem->getshapeId() );
9778       meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9779
9780       // remove medium nodes
9781       for ( size_t i = nbCornerNodes; i < nodes.size(); ++i )
9782         if ( nodes[i]->NbInverseElements() == 0 )
9783           meshDS->RemoveFreeNode( nodes[i], theSm );
9784
9785       // add a linear element
9786       nodes.resize( nbCornerNodes );
9787       SMDS_MeshElement * newElem = AddElement( nodes, elemType );
9788       ReplaceElemInGroups(elem, newElem, meshDS);
9789       if( theSm && newElem )
9790         theSm->AddElement( newElem );
9791     }
9792   }
9793   return nbElem;
9794 }
9795
9796 //=======================================================================
9797 //function : ConvertFromQuadratic
9798 //purpose  :
9799 //=======================================================================
9800
9801 bool SMESH_MeshEditor::ConvertFromQuadratic()
9802 {
9803   int nbCheckedElems = 0;
9804   if ( myMesh->HasShapeToMesh() )
9805   {
9806     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9807     {
9808       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9809       while ( smIt->more() ) {
9810         SMESH_subMesh* sm = smIt->next();
9811         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9812           nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9813       }
9814     }
9815   }
9816
9817   int totalNbElems =
9818     GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9819   if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9820   {
9821     SMESHDS_SubMesh *aSM = 0;
9822     removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9823   }
9824
9825   return true;
9826 }
9827
9828 namespace
9829 {
9830   //================================================================================
9831   /*!
9832    * \brief Return true if all medium nodes of the element are in the node set
9833    */
9834   //================================================================================
9835
9836   bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9837   {
9838     for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9839       if ( !nodeSet.count( elem->GetNode(i) ))
9840         return false;
9841     return true;
9842   }
9843 }
9844
9845 //================================================================================
9846 /*!
9847  * \brief Makes given elements linear
9848  */
9849 //================================================================================
9850
9851 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9852 {
9853   if ( theElements.empty() ) return;
9854
9855   // collect IDs of medium nodes of theElements; some of these nodes will be removed
9856   set<int> mediumNodeIDs;
9857   TIDSortedElemSet::iterator eIt = theElements.begin();
9858   for ( ; eIt != theElements.end(); ++eIt )
9859   {
9860     const SMDS_MeshElement* e = *eIt;
9861     for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9862       mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9863   }
9864
9865   // replace given elements by linear ones
9866   SMDS_ElemIteratorPtr elemIt = elemSetIterator( theElements );
9867   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9868
9869   // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9870   // except those elements sharing medium nodes of quadratic element whose medium nodes
9871   // are not all in mediumNodeIDs
9872
9873   // get remaining medium nodes
9874   TIDSortedNodeSet mediumNodes;
9875   set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9876   for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9877     if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9878       mediumNodes.insert( mediumNodes.end(), n );
9879
9880   // find more quadratic elements to convert
9881   TIDSortedElemSet moreElemsToConvert;
9882   TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9883   for ( ; nIt != mediumNodes.end(); ++nIt )
9884   {
9885     SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9886     while ( invIt->more() )
9887     {
9888       const SMDS_MeshElement* e = invIt->next();
9889       if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9890       {
9891         // find a more complex element including e and
9892         // whose medium nodes are not in mediumNodes
9893         bool complexFound = false;
9894         for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9895         {
9896           SMDS_ElemIteratorPtr invIt2 =
9897             (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9898           while ( invIt2->more() )
9899           {
9900             const SMDS_MeshElement* eComplex = invIt2->next();
9901             if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9902             {
9903               int nbCommonNodes = SMESH_MeshAlgos::GetCommonNodes( e, eComplex ).size();
9904               if ( nbCommonNodes == e->NbNodes())
9905               {
9906                 complexFound = true;
9907                 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9908                 break;
9909               }
9910             }
9911           }
9912         }
9913         if ( !complexFound )
9914           moreElemsToConvert.insert( e );
9915       }
9916     }
9917   }
9918   elemIt = elemSetIterator( moreElemsToConvert );
9919   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9920 }
9921
9922 //=======================================================================
9923 //function : SewSideElements
9924 //purpose  :
9925 //=======================================================================
9926
9927 SMESH_MeshEditor::Sew_Error
9928 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet&    theSide1,
9929                                    TIDSortedElemSet&    theSide2,
9930                                    const SMDS_MeshNode* theFirstNode1,
9931                                    const SMDS_MeshNode* theFirstNode2,
9932                                    const SMDS_MeshNode* theSecondNode1,
9933                                    const SMDS_MeshNode* theSecondNode2)
9934 {
9935   myLastCreatedElems.Clear();
9936   myLastCreatedNodes.Clear();
9937
9938   if ( theSide1.size() != theSide2.size() )
9939     return SEW_DIFF_NB_OF_ELEMENTS;
9940
9941   Sew_Error aResult = SEW_OK;
9942   // Algo:
9943   // 1. Build set of faces representing each side
9944   // 2. Find which nodes of the side 1 to merge with ones on the side 2
9945   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9946
9947   // =======================================================================
9948   // 1. Build set of faces representing each side:
9949   // =======================================================================
9950   // a. build set of nodes belonging to faces
9951   // b. complete set of faces: find missing faces whose nodes are in set of nodes
9952   // c. create temporary faces representing side of volumes if correspondent
9953   //    face does not exist
9954
9955   SMESHDS_Mesh* aMesh = GetMeshDS();
9956   // TODO algorithm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9957   //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9958   TIDSortedElemSet             faceSet1, faceSet2;
9959   set<const SMDS_MeshElement*> volSet1,  volSet2;
9960   set<const SMDS_MeshNode*>    nodeSet1, nodeSet2;
9961   TIDSortedElemSet             * faceSetPtr[] = { &faceSet1, &faceSet2 };
9962   set<const SMDS_MeshElement*> *  volSetPtr[] = { &volSet1,  &volSet2  };
9963   set<const SMDS_MeshNode*>    * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9964   TIDSortedElemSet             * elemSetPtr[] = { &theSide1, &theSide2 };
9965   int iSide, iFace, iNode;
9966
9967   list<const SMDS_MeshElement* > tempFaceList;
9968   for ( iSide = 0; iSide < 2; iSide++ ) {
9969     set<const SMDS_MeshNode*>    * nodeSet = nodeSetPtr[ iSide ];
9970     TIDSortedElemSet             * elemSet = elemSetPtr[ iSide ];
9971     TIDSortedElemSet             * faceSet = faceSetPtr[ iSide ];
9972     set<const SMDS_MeshElement*> * volSet  = volSetPtr [ iSide ];
9973     set<const SMDS_MeshElement*>::iterator vIt;
9974     TIDSortedElemSet::iterator eIt;
9975     set<const SMDS_MeshNode*>::iterator    nIt;
9976
9977     // check that given nodes belong to given elements
9978     const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9979     const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9980     int firstIndex = -1, secondIndex = -1;
9981     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9982       const SMDS_MeshElement* elem = *eIt;
9983       if ( firstIndex  < 0 ) firstIndex  = elem->GetNodeIndex( n1 );
9984       if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9985       if ( firstIndex > -1 && secondIndex > -1 ) break;
9986     }
9987     if ( firstIndex < 0 || secondIndex < 0 ) {
9988       // we can simply return until temporary faces created
9989       return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9990     }
9991
9992     // -----------------------------------------------------------
9993     // 1a. Collect nodes of existing faces
9994     //     and build set of face nodes in order to detect missing
9995     //     faces corresponding to sides of volumes
9996     // -----------------------------------------------------------
9997
9998     set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9999
10000     // loop on the given element of a side
10001     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
10002       //const SMDS_MeshElement* elem = *eIt;
10003       const SMDS_MeshElement* elem = *eIt;
10004       if ( elem->GetType() == SMDSAbs_Face ) {
10005         faceSet->insert( elem );
10006         set <const SMDS_MeshNode*> faceNodeSet;
10007         SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
10008         while ( nodeIt->more() ) {
10009           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10010           nodeSet->insert( n );
10011           faceNodeSet.insert( n );
10012         }
10013         setOfFaceNodeSet.insert( faceNodeSet );
10014       }
10015       else if ( elem->GetType() == SMDSAbs_Volume )
10016         volSet->insert( elem );
10017     }
10018     // ------------------------------------------------------------------------------
10019     // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
10020     // ------------------------------------------------------------------------------
10021
10022     for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10023       SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10024       while ( fIt->more() ) { // loop on faces sharing a node
10025         const SMDS_MeshElement* f = fIt->next();
10026         if ( faceSet->find( f ) == faceSet->end() ) {
10027           // check if all nodes are in nodeSet and
10028           // complete setOfFaceNodeSet if they are
10029           set <const SMDS_MeshNode*> faceNodeSet;
10030           SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10031           bool allInSet = true;
10032           while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10033             const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10034             if ( nodeSet->find( n ) == nodeSet->end() )
10035               allInSet = false;
10036             else
10037               faceNodeSet.insert( n );
10038           }
10039           if ( allInSet ) {
10040             faceSet->insert( f );
10041             setOfFaceNodeSet.insert( faceNodeSet );
10042           }
10043         }
10044       }
10045     }
10046
10047     // -------------------------------------------------------------------------
10048     // 1c. Create temporary faces representing sides of volumes if correspondent
10049     //     face does not exist
10050     // -------------------------------------------------------------------------
10051
10052     if ( !volSet->empty() ) {
10053       //int nodeSetSize = nodeSet->size();
10054
10055       // loop on given volumes
10056       for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
10057         SMDS_VolumeTool vol (*vIt);
10058         // loop on volume faces: find free faces
10059         // --------------------------------------
10060         list<const SMDS_MeshElement* > freeFaceList;
10061         for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
10062           if ( !vol.IsFreeFace( iFace ))
10063             continue;
10064           // check if there is already a face with same nodes in a face set
10065           const SMDS_MeshElement* aFreeFace = 0;
10066           const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
10067           int nbNodes = vol.NbFaceNodes( iFace );
10068           set <const SMDS_MeshNode*> faceNodeSet;
10069           vol.GetFaceNodes( iFace, faceNodeSet );
10070           bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
10071           if ( isNewFace ) {
10072             // no such a face is given but it still can exist, check it
10073             vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
10074             aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
10075           }
10076           if ( !aFreeFace ) {
10077             // create a temporary face
10078             if ( nbNodes == 3 ) {
10079               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
10080               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
10081             }
10082             else if ( nbNodes == 4 ) {
10083               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10084               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10085             }
10086             else {
10087               vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
10088               //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
10089               aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
10090             }
10091             if ( aFreeFace )
10092               tempFaceList.push_back( aFreeFace );
10093           }
10094
10095           if ( aFreeFace )
10096             freeFaceList.push_back( aFreeFace );
10097
10098         } // loop on faces of a volume
10099
10100         // choose one of several free faces of a volume
10101         // --------------------------------------------
10102         if ( freeFaceList.size() > 1 ) {
10103           // choose a face having max nb of nodes shared by other elems of a side
10104           int maxNbNodes = -1;
10105           list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
10106           while ( fIt != freeFaceList.end() ) { // loop on free faces
10107             int nbSharedNodes = 0;
10108             SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10109             while ( nodeIt->more() ) { // loop on free face nodes
10110               const SMDS_MeshNode* n =
10111                 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10112               SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
10113               while ( invElemIt->more() ) {
10114                 const SMDS_MeshElement* e = invElemIt->next();
10115                 nbSharedNodes += faceSet->count( e );
10116                 nbSharedNodes += elemSet->count( e );
10117               }
10118             }
10119             if ( nbSharedNodes > maxNbNodes ) {
10120               maxNbNodes = nbSharedNodes;
10121               freeFaceList.erase( freeFaceList.begin(), fIt++ );
10122             }
10123             else if ( nbSharedNodes == maxNbNodes ) {
10124               fIt++;
10125             }
10126             else {
10127               freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
10128             }
10129           }
10130           if ( freeFaceList.size() > 1 )
10131           {
10132             // could not choose one face, use another way
10133             // choose a face most close to the bary center of the opposite side
10134             gp_XYZ aBC( 0., 0., 0. );
10135             set <const SMDS_MeshNode*> addedNodes;
10136             TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
10137             eIt = elemSet2->begin();
10138             for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
10139               SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
10140               while ( nodeIt->more() ) { // loop on free face nodes
10141                 const SMDS_MeshNode* n =
10142                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10143                 if ( addedNodes.insert( n ).second )
10144                   aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
10145               }
10146             }
10147             aBC /= addedNodes.size();
10148             double minDist = DBL_MAX;
10149             fIt = freeFaceList.begin();
10150             while ( fIt != freeFaceList.end() ) { // loop on free faces
10151               double dist = 0;
10152               SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10153               while ( nodeIt->more() ) { // loop on free face nodes
10154                 const SMDS_MeshNode* n =
10155                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10156                 gp_XYZ p( n->X(),n->Y(),n->Z() );
10157                 dist += ( aBC - p ).SquareModulus();
10158               }
10159               if ( dist < minDist ) {
10160                 minDist = dist;
10161                 freeFaceList.erase( freeFaceList.begin(), fIt++ );
10162               }
10163               else
10164                 fIt = freeFaceList.erase( fIt++ );
10165             }
10166           }
10167         } // choose one of several free faces of a volume
10168
10169         if ( freeFaceList.size() == 1 ) {
10170           const SMDS_MeshElement* aFreeFace = freeFaceList.front();
10171           faceSet->insert( aFreeFace );
10172           // complete a node set with nodes of a found free face
10173           //           for ( iNode = 0; iNode < ; iNode++ )
10174           //             nodeSet->insert( fNodes[ iNode ] );
10175         }
10176
10177       } // loop on volumes of a side
10178
10179       //       // complete a set of faces if new nodes in a nodeSet appeared
10180       //       // ----------------------------------------------------------
10181       //       if ( nodeSetSize != nodeSet->size() ) {
10182       //         for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10183       //           SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10184       //           while ( fIt->more() ) { // loop on faces sharing a node
10185       //             const SMDS_MeshElement* f = fIt->next();
10186       //             if ( faceSet->find( f ) == faceSet->end() ) {
10187       //               // check if all nodes are in nodeSet and
10188       //               // complete setOfFaceNodeSet if they are
10189       //               set <const SMDS_MeshNode*> faceNodeSet;
10190       //               SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10191       //               bool allInSet = true;
10192       //               while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10193       //                 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10194       //                 if ( nodeSet->find( n ) == nodeSet->end() )
10195       //                   allInSet = false;
10196       //                 else
10197       //                   faceNodeSet.insert( n );
10198       //               }
10199       //               if ( allInSet ) {
10200       //                 faceSet->insert( f );
10201       //                 setOfFaceNodeSet.insert( faceNodeSet );
10202       //               }
10203       //             }
10204       //           }
10205       //         }
10206       //       }
10207     } // Create temporary faces, if there are volumes given
10208   } // loop on sides
10209
10210   if ( faceSet1.size() != faceSet2.size() ) {
10211     // delete temporary faces: they are in reverseElements of actual nodes
10212 //    SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10213 //    while ( tmpFaceIt->more() )
10214 //      aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10215 //    list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10216 //    for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10217 //      aMesh->RemoveElement(*tmpFaceIt);
10218     MESSAGE("Diff nb of faces");
10219     return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10220   }
10221
10222   // ============================================================
10223   // 2. Find nodes to merge:
10224   //              bind a node to remove to a node to put instead
10225   // ============================================================
10226
10227   TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
10228   if ( theFirstNode1 != theFirstNode2 )
10229     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10230   if ( theSecondNode1 != theSecondNode2 )
10231     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10232
10233   LinkID_Gen aLinkID_Gen( GetMeshDS() );
10234   set< long > linkIdSet; // links to process
10235   linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
10236
10237   typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
10238   list< NLink > linkList[2];
10239   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10240   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10241   // loop on links in linkList; find faces by links and append links
10242   // of the found faces to linkList
10243   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10244   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
10245   {
10246     NLink link[] = { *linkIt[0], *linkIt[1] };
10247     long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
10248     if ( !linkIdSet.count( linkID ) )
10249       continue;
10250
10251     // by links, find faces in the face sets,
10252     // and find indices of link nodes in the found faces;
10253     // in a face set, there is only one or no face sharing a link
10254     // ---------------------------------------------------------------
10255
10256     const SMDS_MeshElement* face[] = { 0, 0 };
10257     vector<const SMDS_MeshNode*> fnodes[2];
10258     int iLinkNode[2][2];
10259     TIDSortedElemSet avoidSet;
10260     for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10261       const SMDS_MeshNode* n1 = link[iSide].first;
10262       const SMDS_MeshNode* n2 = link[iSide].second;
10263       //cout << "Side " << iSide << " ";
10264       //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
10265       // find a face by two link nodes
10266       face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
10267                                                       *faceSetPtr[ iSide ], avoidSet,
10268                                                       &iLinkNode[iSide][0],
10269                                                       &iLinkNode[iSide][1] );
10270       if ( face[ iSide ])
10271       {
10272         //cout << " F " << face[ iSide]->GetID() <<endl;
10273         faceSetPtr[ iSide ]->erase( face[ iSide ]);
10274         // put face nodes to fnodes
10275         if ( face[ iSide ]->IsQuadratic() )
10276         {
10277           // use interlaced nodes iterator
10278           const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>( face[ iSide ]);
10279           if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
10280           SMDS_ElemIteratorPtr nIter = F->interlacedNodesElemIterator();
10281           while ( nIter->more() )
10282             fnodes[ iSide ].push_back( cast2Node( nIter->next() ));
10283         }
10284         else
10285         {
10286           fnodes[ iSide ].assign( face[ iSide ]->begin_nodes(),
10287                                   face[ iSide ]->end_nodes() );
10288         }
10289         fnodes[ iSide ].push_back( fnodes[ iSide ].front());
10290       }
10291     }
10292
10293     // check similarity of elements of the sides
10294     if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10295       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10296       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10297         aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10298       }
10299       else {
10300         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10301       }
10302       break; // do not return because it's necessary to remove tmp faces
10303     }
10304
10305     // set nodes to merge
10306     // -------------------
10307
10308     if ( face[0] && face[1] )  {
10309       const int nbNodes = face[0]->NbNodes();
10310       if ( nbNodes != face[1]->NbNodes() ) {
10311         MESSAGE("Diff nb of face nodes");
10312         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10313         break; // do not return because it s necessary to remove tmp faces
10314       }
10315       bool reverse[] = { false, false }; // order of nodes in the link
10316       for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10317         // analyse link orientation in faces
10318         int i1 = iLinkNode[ iSide ][ 0 ];
10319         int i2 = iLinkNode[ iSide ][ 1 ];
10320         reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10321       }
10322       int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
10323       int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
10324       for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
10325       {
10326         nReplaceMap.insert  ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
10327                                           fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
10328       }
10329
10330       // add other links of the faces to linkList
10331       // -----------------------------------------
10332
10333       for ( iNode = 0; iNode < nbNodes; iNode++ )  {
10334         linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
10335         pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10336         if ( !iter_isnew.second ) { // already in a set: no need to process
10337           linkIdSet.erase( iter_isnew.first );
10338         }
10339         else // new in set == encountered for the first time: add
10340         {
10341           const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
10342           const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
10343           linkList[0].push_back ( NLink( n1, n2 ));
10344           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10345         }
10346       }
10347     } // 2 faces found
10348
10349     if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
10350       break;
10351
10352   } // loop on link lists
10353
10354   if ( aResult == SEW_OK &&
10355        ( //linkIt[0] != linkList[0].end() ||
10356          !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10357     MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10358              " " << (faceSetPtr[1]->empty()));
10359     aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10360   }
10361
10362   // ====================================================================
10363   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10364   // ====================================================================
10365
10366   // delete temporary faces
10367 //  SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10368 //  while ( tmpFaceIt->more() )
10369 //    aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10370   list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10371   for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10372     aMesh->RemoveElement(*tmpFaceIt);
10373
10374   if ( aResult != SEW_OK)
10375     return aResult;
10376
10377   list< int > nodeIDsToRemove;
10378   vector< const SMDS_MeshNode*> nodes;
10379   ElemFeatures elemType;
10380
10381   // loop on nodes replacement map
10382   TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10383   for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10384     if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second )
10385     {
10386       const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10387       nodeIDsToRemove.push_back( nToRemove->GetID() );
10388       // loop on elements sharing nToRemove
10389       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10390       while ( invElemIt->more() ) {
10391         const SMDS_MeshElement* e = invElemIt->next();
10392         // get a new suite of nodes: make replacement
10393         int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10394         nodes.resize( nbNodes );
10395         SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10396         while ( nIt->more() ) {
10397           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
10398           nnIt = nReplaceMap.find( n );
10399           if ( nnIt != nReplaceMap.end() ) {
10400             nbReplaced++;
10401             n = (*nnIt).second;
10402           }
10403           nodes[ i++ ] = n;
10404         }
10405         //       if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10406         //         elemIDsToRemove.push_back( e->GetID() );
10407         //       else
10408         if ( nbReplaced )
10409         {
10410           elemType.Init( e, /*basicOnly=*/false ).SetID( e->GetID() );
10411           aMesh->RemoveElement( e );
10412
10413           if ( SMDS_MeshElement* newElem = this->AddElement( nodes, elemType ))
10414           {
10415             AddToSameGroups( newElem, e, aMesh );
10416             if ( int aShapeId = e->getshapeId() )
10417               aMesh->SetMeshElementOnShape( newElem, aShapeId );
10418           }
10419         }
10420       }
10421     }
10422
10423   Remove( nodeIDsToRemove, true );
10424
10425   return aResult;
10426 }
10427
10428 //================================================================================
10429 /*!
10430  * \brief Find corresponding nodes in two sets of faces
10431  * \param theSide1 - first face set
10432  * \param theSide2 - second first face
10433  * \param theFirstNode1 - a boundary node of set 1
10434  * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10435  * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10436  * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10437  * \param nReplaceMap - output map of corresponding nodes
10438  * \return bool  - is a success or not
10439  */
10440 //================================================================================
10441
10442 #ifdef _DEBUG_
10443 //#define DEBUG_MATCHING_NODES
10444 #endif
10445
10446 SMESH_MeshEditor::Sew_Error
10447 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10448                                     set<const SMDS_MeshElement*>& theSide2,
10449                                     const SMDS_MeshNode*          theFirstNode1,
10450                                     const SMDS_MeshNode*          theFirstNode2,
10451                                     const SMDS_MeshNode*          theSecondNode1,
10452                                     const SMDS_MeshNode*          theSecondNode2,
10453                                     TNodeNodeMap &                nReplaceMap)
10454 {
10455   set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10456
10457   nReplaceMap.clear();
10458   if ( theFirstNode1 != theFirstNode2 )
10459     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10460   if ( theSecondNode1 != theSecondNode2 )
10461     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10462
10463   set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10464   linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10465
10466   list< NLink > linkList[2];
10467   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10468   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10469
10470   // loop on links in linkList; find faces by links and append links
10471   // of the found faces to linkList
10472   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10473   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10474     NLink link[] = { *linkIt[0], *linkIt[1] };
10475     if ( linkSet.find( link[0] ) == linkSet.end() )
10476       continue;
10477
10478     // by links, find faces in the face sets,
10479     // and find indices of link nodes in the found faces;
10480     // in a face set, there is only one or no face sharing a link
10481     // ---------------------------------------------------------------
10482
10483     const SMDS_MeshElement* face[] = { 0, 0 };
10484     list<const SMDS_MeshNode*> notLinkNodes[2];
10485     //bool reverse[] = { false, false }; // order of notLinkNodes
10486     int nbNodes[2];
10487     for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10488     {
10489       const SMDS_MeshNode* n1 = link[iSide].first;
10490       const SMDS_MeshNode* n2 = link[iSide].second;
10491       set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10492       set< const SMDS_MeshElement* > facesOfNode1;
10493       for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10494       {
10495         // during a loop of the first node, we find all faces around n1,
10496         // during a loop of the second node, we find one face sharing both n1 and n2
10497         const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10498         SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10499         while ( fIt->more() ) { // loop on faces sharing a node
10500           const SMDS_MeshElement* f = fIt->next();
10501           if (faceSet->find( f ) != faceSet->end() && // f is in face set
10502               ! facesOfNode1.insert( f ).second ) // f encounters twice
10503           {
10504             if ( face[ iSide ] ) {
10505               MESSAGE( "2 faces per link " );
10506               return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10507             }
10508             face[ iSide ] = f;
10509             faceSet->erase( f );
10510
10511             // get not link nodes
10512             int nbN = f->NbNodes();
10513             if ( f->IsQuadratic() )
10514               nbN /= 2;
10515             nbNodes[ iSide ] = nbN;
10516             list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10517             int i1 = f->GetNodeIndex( n1 );
10518             int i2 = f->GetNodeIndex( n2 );
10519             int iEnd = nbN, iBeg = -1, iDelta = 1;
10520             bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10521             if ( reverse ) {
10522               std::swap( iEnd, iBeg ); iDelta = -1;
10523             }
10524             int i = i2;
10525             while ( true ) {
10526               i += iDelta;
10527               if ( i == iEnd ) i = iBeg + iDelta;
10528               if ( i == i1 ) break;
10529               nodes.push_back ( f->GetNode( i ) );
10530             }
10531           }
10532         }
10533       }
10534     }
10535     // check similarity of elements of the sides
10536     if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10537       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10538       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10539         return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10540       }
10541       else {
10542         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10543       }
10544     }
10545
10546     // set nodes to merge
10547     // -------------------
10548
10549     if ( face[0] && face[1] )  {
10550       if ( nbNodes[0] != nbNodes[1] ) {
10551         MESSAGE("Diff nb of face nodes");
10552         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10553       }
10554 #ifdef DEBUG_MATCHING_NODES
10555       MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10556                 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10557                 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10558 #endif
10559       int nbN = nbNodes[0];
10560       {
10561         list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10562         list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10563         for ( int i = 0 ; i < nbN - 2; ++i ) {
10564 #ifdef DEBUG_MATCHING_NODES
10565           MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10566 #endif
10567           nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10568         }
10569       }
10570
10571       // add other links of the face 1 to linkList
10572       // -----------------------------------------
10573
10574       const SMDS_MeshElement* f0 = face[0];
10575       const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10576       for ( int i = 0; i < nbN; i++ )
10577       {
10578         const SMDS_MeshNode* n2 = f0->GetNode( i );
10579         pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10580           linkSet.insert( SMESH_TLink( n1, n2 ));
10581         if ( !iter_isnew.second ) { // already in a set: no need to process
10582           linkSet.erase( iter_isnew.first );
10583         }
10584         else // new in set == encountered for the first time: add
10585         {
10586 #ifdef DEBUG_MATCHING_NODES
10587           MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10588                     << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10589 #endif
10590           linkList[0].push_back ( NLink( n1, n2 ));
10591           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10592         }
10593         n1 = n2;
10594       }
10595     } // 2 faces found
10596   } // loop on link lists
10597
10598   return SEW_OK;
10599 }
10600
10601 //================================================================================
10602 /*!
10603  * \brief Create elements equal (on same nodes) to given ones
10604  *  \param [in] theElements - a set of elems to duplicate. If it is empty, all
10605  *              elements of the uppest dimension are duplicated.
10606  */
10607 //================================================================================
10608
10609 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
10610 {
10611   ClearLastCreated();
10612   SMESHDS_Mesh* mesh = GetMeshDS();
10613
10614   // get an element type and an iterator over elements
10615
10616   SMDSAbs_ElementType type = SMDSAbs_All;
10617   SMDS_ElemIteratorPtr elemIt;
10618   vector< const SMDS_MeshElement* > allElems;
10619   if ( theElements.empty() )
10620   {
10621     if ( mesh->NbNodes() == 0 )
10622       return;
10623     // get most complex type
10624     SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10625       SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10626       SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10627     };
10628     for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10629       if ( mesh->GetMeshInfo().NbElements( types[i] ))
10630       {
10631         type = types[i];
10632         break;
10633       }
10634     // put all elements in the vector <allElems>
10635     allElems.reserve( mesh->GetMeshInfo().NbElements( type ));
10636     elemIt = mesh->elementsIterator( type );
10637     while ( elemIt->more() )
10638       allElems.push_back( elemIt->next());
10639     elemIt = elemSetIterator( allElems );
10640   }
10641   else
10642   {
10643     type = (*theElements.begin())->GetType();
10644     elemIt = elemSetIterator( theElements );
10645   }
10646
10647   // duplicate elements
10648
10649   ElemFeatures elemType;
10650
10651   vector< const SMDS_MeshNode* > nodes;
10652   while ( elemIt->more() )
10653   {
10654     const SMDS_MeshElement* elem = elemIt->next();
10655     if ( elem->GetType() != type )
10656       continue;
10657
10658     elemType.Init( elem, /*basicOnly=*/false );
10659     nodes.assign( elem->begin_nodes(), elem->end_nodes() );
10660
10661     AddElement( nodes, elemType );
10662   }
10663 }
10664
10665 //================================================================================
10666 /*!
10667   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10668   \param theElems - the list of elements (edges or faces) to be replicated
10669   The nodes for duplication could be found from these elements
10670   \param theNodesNot - list of nodes to NOT replicate
10671   \param theAffectedElems - the list of elements (cells and edges) to which the
10672   replicated nodes should be associated to.
10673   \return TRUE if operation has been completed successfully, FALSE otherwise
10674 */
10675 //================================================================================
10676
10677 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10678                                     const TIDSortedElemSet& theNodesNot,
10679                                     const TIDSortedElemSet& theAffectedElems )
10680 {
10681   myLastCreatedElems.Clear();
10682   myLastCreatedNodes.Clear();
10683
10684   if ( theElems.size() == 0 )
10685     return false;
10686
10687   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10688   if ( !aMeshDS )
10689     return false;
10690
10691   bool res = false;
10692   TNodeNodeMap anOldNodeToNewNode;
10693   // duplicate elements and nodes
10694   res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10695   // replce nodes by duplications
10696   res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10697   return res;
10698 }
10699
10700 //================================================================================
10701 /*!
10702   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10703   \param theMeshDS - mesh instance
10704   \param theElems - the elements replicated or modified (nodes should be changed)
10705   \param theNodesNot - nodes to NOT replicate
10706   \param theNodeNodeMap - relation of old node to new created node
10707   \param theIsDoubleElem - flag os to replicate element or modify
10708   \return TRUE if operation has been completed successfully, FALSE otherwise
10709 */
10710 //================================================================================
10711
10712 bool SMESH_MeshEditor::doubleNodes(SMESHDS_Mesh*           theMeshDS,
10713                                    const TIDSortedElemSet& theElems,
10714                                    const TIDSortedElemSet& theNodesNot,
10715                                    TNodeNodeMap&           theNodeNodeMap,
10716                                    const bool              theIsDoubleElem )
10717 {
10718   // iterate through element and duplicate them (by nodes duplication)
10719   bool res = false;
10720   std::vector<const SMDS_MeshNode*> newNodes;
10721   ElemFeatures elemType;
10722
10723   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10724   for ( ;  elemItr != theElems.end(); ++elemItr )
10725   {
10726     const SMDS_MeshElement* anElem = *elemItr;
10727     if (!anElem)
10728       continue;
10729
10730     // duplicate nodes to duplicate element
10731     bool isDuplicate = false;
10732     newNodes.resize( anElem->NbNodes() );
10733     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10734     int ind = 0;
10735     while ( anIter->more() )
10736     {
10737       const SMDS_MeshNode* aCurrNode = static_cast<const SMDS_MeshNode*>( anIter->next() );
10738       const SMDS_MeshNode*  aNewNode = aCurrNode;
10739       TNodeNodeMap::iterator     n2n = theNodeNodeMap.find( aCurrNode );
10740       if ( n2n != theNodeNodeMap.end() )
10741       {
10742         aNewNode = n2n->second;
10743       }
10744       else if ( theIsDoubleElem && !theNodesNot.count( aCurrNode ))
10745       {
10746         // duplicate node
10747         aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10748         copyPosition( aCurrNode, aNewNode );
10749         theNodeNodeMap[ aCurrNode ] = aNewNode;
10750         myLastCreatedNodes.Append( aNewNode );
10751       }
10752       isDuplicate |= (aCurrNode != aNewNode);
10753       newNodes[ ind++ ] = aNewNode;
10754     }
10755     if ( !isDuplicate )
10756       continue;
10757
10758     if ( theIsDoubleElem )
10759       AddElement( newNodes, elemType.Init( anElem, /*basicOnly=*/false ));
10760     else
10761       theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], newNodes.size() );
10762
10763     res = true;
10764   }
10765   return res;
10766 }
10767
10768 //================================================================================
10769 /*!
10770   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10771   \param theNodes - identifiers of nodes to be doubled
10772   \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10773   nodes. If list of element identifiers is empty then nodes are doubled but
10774   they not assigned to elements
10775   \return TRUE if operation has been completed successfully, FALSE otherwise
10776 */
10777 //================================================================================
10778
10779 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10780                                     const std::list< int >& theListOfModifiedElems )
10781 {
10782   myLastCreatedElems.Clear();
10783   myLastCreatedNodes.Clear();
10784
10785   if ( theListOfNodes.size() == 0 )
10786     return false;
10787
10788   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10789   if ( !aMeshDS )
10790     return false;
10791
10792   // iterate through nodes and duplicate them
10793
10794   std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10795
10796   std::list< int >::const_iterator aNodeIter;
10797   for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10798   {
10799     int aCurr = *aNodeIter;
10800     SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr );
10801     if ( !aNode )
10802       continue;
10803
10804     // duplicate node
10805
10806     const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10807     if ( aNewNode )
10808     {
10809       copyPosition( aNode, aNewNode );
10810       anOldNodeToNewNode[ aNode ] = aNewNode;
10811       myLastCreatedNodes.Append( aNewNode );
10812     }
10813   }
10814
10815   // Create map of new nodes for modified elements
10816
10817   std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> > anElemToNodes;
10818
10819   std::list< int >::const_iterator anElemIter;
10820   for ( anElemIter = theListOfModifiedElems.begin();
10821         anElemIter != theListOfModifiedElems.end(); ++anElemIter )
10822   {
10823     int aCurr = *anElemIter;
10824     SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr );
10825     if ( !anElem )
10826       continue;
10827
10828     vector<const SMDS_MeshNode*> aNodeArr( anElem->NbNodes() );
10829
10830     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10831     int ind = 0;
10832     while ( anIter->more() )
10833     {
10834       SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10835       if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() )
10836       {
10837         const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ];
10838         aNodeArr[ ind++ ] = aNewNode;
10839       }
10840       else
10841         aNodeArr[ ind++ ] = aCurrNode;
10842     }
10843     anElemToNodes[ anElem ] = aNodeArr;
10844   }
10845
10846   // Change nodes of elements
10847
10848   std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> >::iterator
10849     anElemToNodesIter = anElemToNodes.begin();
10850   for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter )
10851   {
10852     const SMDS_MeshElement* anElem = anElemToNodesIter->first;
10853     vector<const SMDS_MeshNode*> aNodeArr = anElemToNodesIter->second;
10854     if ( anElem )
10855     {
10856       aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() );
10857     }
10858   }
10859
10860   return true;
10861 }
10862
10863 namespace {
10864
10865   //================================================================================
10866   /*!
10867   \brief Check if element located inside shape
10868   \return TRUE if IN or ON shape, FALSE otherwise
10869   */
10870   //================================================================================
10871
10872   template<class Classifier>
10873   bool isInside(const SMDS_MeshElement* theElem,
10874                 Classifier&             theClassifier,
10875                 const double            theTol)
10876   {
10877     gp_XYZ centerXYZ (0, 0, 0);
10878     SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
10879     while (aNodeItr->more())
10880       centerXYZ += SMESH_TNodeXYZ(cast2Node( aNodeItr->next()));
10881
10882     gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10883     theClassifier.Perform(aPnt, theTol);
10884     TopAbs_State aState = theClassifier.State();
10885     return (aState == TopAbs_IN || aState == TopAbs_ON );
10886   }
10887
10888   //================================================================================
10889   /*!
10890    * \brief Classifier of the 3D point on the TopoDS_Face
10891    *        with interaface suitable for isInside()
10892    */
10893   //================================================================================
10894
10895   struct _FaceClassifier
10896   {
10897     Extrema_ExtPS       _extremum;
10898     BRepAdaptor_Surface _surface;
10899     TopAbs_State        _state;
10900
10901     _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10902     {
10903       _extremum.Initialize( _surface,
10904                             _surface.FirstUParameter(), _surface.LastUParameter(),
10905                             _surface.FirstVParameter(), _surface.LastVParameter(),
10906                             _surface.Tolerance(), _surface.Tolerance() );
10907     }
10908     void Perform(const gp_Pnt& aPnt, double theTol)
10909     {
10910       theTol *= theTol;
10911       _state = TopAbs_OUT;
10912       _extremum.Perform(aPnt);
10913       if ( _extremum.IsDone() )
10914         for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10915           _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10916     }
10917     TopAbs_State State() const
10918     {
10919       return _state;
10920     }
10921   };
10922 }
10923
10924 //================================================================================
10925 /*!
10926   \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
10927   This method is the first step of DoubleNodeElemGroupsInRegion.
10928   \param theElems - list of groups of elements (edges or faces) to be replicated
10929   \param theNodesNot - list of groups of nodes not to replicated
10930   \param theShape - shape to detect affected elements (element which geometric center
10931          located on or inside shape). If the shape is null, detection is done on faces orientations
10932          (select elements with a gravity center on the side given by faces normals).
10933          This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
10934          The replicated nodes should be associated to affected elements.
10935   \return groups of affected elements
10936   \sa DoubleNodeElemGroupsInRegion()
10937  */
10938 //================================================================================
10939
10940 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
10941                                                    const TIDSortedElemSet& theNodesNot,
10942                                                    const TopoDS_Shape&     theShape,
10943                                                    TIDSortedElemSet&       theAffectedElems)
10944 {
10945   if ( theShape.IsNull() )
10946   {
10947     std::set<const SMDS_MeshNode*> alreadyCheckedNodes;
10948     std::set<const SMDS_MeshElement*> alreadyCheckedElems;
10949     std::set<const SMDS_MeshElement*> edgesToCheck;
10950     alreadyCheckedNodes.clear();
10951     alreadyCheckedElems.clear();
10952     edgesToCheck.clear();
10953
10954     // --- iterates on elements to be replicated and get elements by back references from their nodes
10955
10956     TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10957     for ( ;  elemItr != theElems.end(); ++elemItr )
10958     {
10959       SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10960       if (!anElem || (anElem->GetType() != SMDSAbs_Face))
10961         continue;
10962       gp_XYZ normal;
10963       SMESH_MeshAlgos::FaceNormal( anElem, normal, /*normalized=*/true );
10964       std::set<const SMDS_MeshNode*> nodesElem;
10965       nodesElem.clear();
10966       SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10967       while ( nodeItr->more() )
10968       {
10969         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10970         nodesElem.insert(aNode);
10971       }
10972       std::set<const SMDS_MeshNode*>::iterator nodit = nodesElem.begin();
10973       for (; nodit != nodesElem.end(); nodit++)
10974       {
10975         const SMDS_MeshNode* aNode = *nodit;
10976         if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10977           continue;
10978         if (alreadyCheckedNodes.find(aNode) != alreadyCheckedNodes.end())
10979           continue;
10980         alreadyCheckedNodes.insert(aNode);
10981         SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10982         while ( backElemItr->more() )
10983         {
10984           const SMDS_MeshElement* curElem = backElemItr->next();
10985           if (alreadyCheckedElems.find(curElem) != alreadyCheckedElems.end())
10986             continue;
10987           if (theElems.find(curElem) != theElems.end())
10988             continue;
10989           alreadyCheckedElems.insert(curElem);
10990           double x=0, y=0, z=0;
10991           int nb = 0;
10992           SMDS_ElemIteratorPtr nodeItr2 = curElem->nodesIterator();
10993           while ( nodeItr2->more() )
10994           {
10995             const SMDS_MeshNode* anotherNode = cast2Node(nodeItr2->next());
10996             x += anotherNode->X();
10997             y += anotherNode->Y();
10998             z += anotherNode->Z();
10999             nb++;
11000           }
11001           gp_XYZ p;
11002           p.SetCoord( x/nb -aNode->X(),
11003                       y/nb -aNode->Y(),
11004                       z/nb -aNode->Z() );
11005           if (normal*p > 0)
11006           {
11007             theAffectedElems.insert( curElem );
11008           }
11009           else if (curElem->GetType() == SMDSAbs_Edge)
11010             edgesToCheck.insert(curElem);
11011         }
11012       }
11013     }
11014     // --- add also edges lying on the set of faces (all nodes in alreadyCheckedNodes)
11015     std::set<const SMDS_MeshElement*>::iterator eit = edgesToCheck.begin();
11016     for( ; eit != edgesToCheck.end(); eit++)
11017     {
11018       bool onside = true;
11019       const SMDS_MeshElement* anEdge = *eit;
11020       SMDS_ElemIteratorPtr nodeItr = anEdge->nodesIterator();
11021       while ( nodeItr->more() )
11022       {
11023         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11024         if (alreadyCheckedNodes.find(aNode) == alreadyCheckedNodes.end())
11025         {
11026           onside = false;
11027           break;
11028         }
11029       }
11030       if (onside)
11031       {
11032         theAffectedElems.insert(anEdge);
11033       }
11034     }
11035   }
11036   else
11037   {
11038     const double aTol = Precision::Confusion();
11039     auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
11040     auto_ptr<_FaceClassifier>              aFaceClassifier;
11041     if ( theShape.ShapeType() == TopAbs_SOLID )
11042     {
11043       bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
11044       bsc3d->PerformInfinitePoint(aTol);
11045     }
11046     else if (theShape.ShapeType() == TopAbs_FACE )
11047     {
11048       aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
11049     }
11050
11051     // iterates on indicated elements and get elements by back references from their nodes
11052     TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11053     for ( ;  elemItr != theElems.end(); ++elemItr )
11054     {
11055       SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11056       if (!anElem)
11057         continue;
11058       SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11059       while ( nodeItr->more() )
11060       {
11061         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11062         if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11063           continue;
11064         SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11065         while ( backElemItr->more() )
11066         {
11067           const SMDS_MeshElement* curElem = backElemItr->next();
11068           if ( curElem && theElems.find(curElem) == theElems.end() &&
11069               ( bsc3d.get() ?
11070                 isInside( curElem, *bsc3d, aTol ) :
11071                 isInside( curElem, *aFaceClassifier, aTol )))
11072             theAffectedElems.insert( curElem );
11073         }
11074       }
11075     }
11076   }
11077   return true;
11078 }
11079
11080 //================================================================================
11081 /*!
11082   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11083   \param theElems - group of of elements (edges or faces) to be replicated
11084   \param theNodesNot - group of nodes not to replicate
11085   \param theShape - shape to detect affected elements (element which geometric center
11086   located on or inside shape).
11087   The replicated nodes should be associated to affected elements.
11088   \return TRUE if operation has been completed successfully, FALSE otherwise
11089 */
11090 //================================================================================
11091
11092 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
11093                                             const TIDSortedElemSet& theNodesNot,
11094                                             const TopoDS_Shape&     theShape )
11095 {
11096   if ( theShape.IsNull() )
11097     return false;
11098
11099   const double aTol = Precision::Confusion();
11100   SMESHUtils::Deleter< BRepClass3d_SolidClassifier> bsc3d;
11101   SMESHUtils::Deleter<_FaceClassifier>              aFaceClassifier;
11102   if ( theShape.ShapeType() == TopAbs_SOLID )
11103   {
11104     bsc3d._obj = new BRepClass3d_SolidClassifier( theShape );
11105     bsc3d->PerformInfinitePoint(aTol);
11106   }
11107   else if (theShape.ShapeType() == TopAbs_FACE )
11108   {
11109     aFaceClassifier._obj = new _FaceClassifier( TopoDS::Face( theShape ));
11110   }
11111
11112   // iterates on indicated elements and get elements by back references from their nodes
11113   TIDSortedElemSet anAffected;
11114   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11115   for ( ;  elemItr != theElems.end(); ++elemItr )
11116   {
11117     SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11118     if (!anElem)
11119       continue;
11120
11121     SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11122     while ( nodeItr->more() )
11123     {
11124       const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11125       if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11126         continue;
11127       SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11128       while ( backElemItr->more() )
11129       {
11130         const SMDS_MeshElement* curElem = backElemItr->next();
11131         if ( curElem && theElems.find(curElem) == theElems.end() &&
11132              ( bsc3d ?
11133                isInside( curElem, *bsc3d, aTol ) :
11134                isInside( curElem, *aFaceClassifier, aTol )))
11135           anAffected.insert( curElem );
11136       }
11137     }
11138   }
11139   return DoubleNodes( theElems, theNodesNot, anAffected );
11140 }
11141
11142 /*!
11143  *  \brief compute an oriented angle between two planes defined by four points.
11144  *  The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
11145  *  @param p0 base of the rotation axe
11146  *  @param p1 extremity of the rotation axe
11147  *  @param g1 belongs to the first plane
11148  *  @param g2 belongs to the second plane
11149  */
11150 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
11151 {
11152   gp_Vec vref(p0, p1);
11153   gp_Vec v1(p0, g1);
11154   gp_Vec v2(p0, g2);
11155   gp_Vec n1 = vref.Crossed(v1);
11156   gp_Vec n2 = vref.Crossed(v2);
11157   try {
11158     return n2.AngleWithRef(n1, vref);
11159   }
11160   catch ( Standard_Failure ) {
11161   }
11162   return Max( v1.Magnitude(), v2.Magnitude() );
11163 }
11164
11165 /*!
11166  * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11167  *  The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
11168  * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
11169  * Triangles are transformed into prisms, and quadrangles into hexahedrons.
11170  * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
11171  * 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.
11172  * 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.
11173  * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
11174  * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
11175  * \param theElems - list of groups of volumes, where a group of volume is a set of
11176  *        SMDS_MeshElements sorted by Id.
11177  * \param createJointElems - if TRUE, create the elements
11178  * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
11179  *        the boundary between \a theDomains and the rest mesh
11180  * \return TRUE if operation has been completed successfully, FALSE otherwise
11181  */
11182 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11183                                                      bool                                 createJointElems,
11184                                                      bool                                 onAllBoundaries)
11185 {
11186   // MESSAGE("----------------------------------------------");
11187   // MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11188   // MESSAGE("----------------------------------------------");
11189
11190   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11191   meshDS->BuildDownWardConnectivity(true);
11192   CHRONO(50);
11193   SMDS_UnstructuredGrid *grid = meshDS->getGrid();
11194
11195   // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11196   //     build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11197   //     build the list of nodes shared by 2 or more domains, with their domain indexes
11198
11199   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11200   std::map<int,int>celldom; // cell vtkId --> domain
11201   std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains;  // oldNode --> (id domain --> id cell)
11202   std::map<int, std::map<int,int> > nodeDomains; // oldId -->  (domainId --> newId)
11203   faceDomains.clear();
11204   celldom.clear();
11205   cellDomains.clear();
11206   nodeDomains.clear();
11207   std::map<int,int> emptyMap;
11208   std::set<int> emptySet;
11209   emptyMap.clear();
11210
11211   //MESSAGE(".. Number of domains :"<<theElems.size());
11212
11213   TIDSortedElemSet theRestDomElems;
11214   const int iRestDom  = -1;
11215   const int idom0     = onAllBoundaries ? iRestDom : 0;
11216   const int nbDomains = theElems.size();
11217
11218   // Check if the domains do not share an element
11219   for (int idom = 0; idom < nbDomains-1; idom++)
11220   {
11221     //       MESSAGE("... Check of domain #" << idom);
11222     const TIDSortedElemSet& domain = theElems[idom];
11223     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11224     for (; elemItr != domain.end(); ++elemItr)
11225     {
11226       const SMDS_MeshElement* anElem = *elemItr;
11227       int idombisdeb = idom + 1 ;
11228       // check if the element belongs to a domain further in the list
11229       for ( size_t idombis = idombisdeb; idombis < theElems.size(); idombis++ )
11230       {
11231         const TIDSortedElemSet& domainbis = theElems[idombis];
11232         if ( domainbis.count( anElem ))
11233         {
11234           MESSAGE(".... Domain #" << idom);
11235           MESSAGE(".... Domain #" << idombis);
11236           throw SALOME_Exception("The domains are not disjoint.");
11237           return false ;
11238         }
11239       }
11240     }
11241   }
11242
11243   for (int idom = 0; idom < nbDomains; idom++)
11244   {
11245
11246     // --- build a map (face to duplicate --> volume to modify)
11247     //     with all the faces shared by 2 domains (group of elements)
11248     //     and corresponding volume of this domain, for each shared face.
11249     //     a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11250
11251     //MESSAGE("... Neighbors of domain #" << idom);
11252     const TIDSortedElemSet& domain = theElems[idom];
11253     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11254     for (; elemItr != domain.end(); ++elemItr)
11255     {
11256       const SMDS_MeshElement* anElem = *elemItr;
11257       if (!anElem)
11258         continue;
11259       int vtkId = anElem->getVtkId();
11260       //MESSAGE("  vtkId " << vtkId << " smdsId " << anElem->GetID());
11261       int neighborsVtkIds[NBMAXNEIGHBORS];
11262       int downIds[NBMAXNEIGHBORS];
11263       unsigned char downTypes[NBMAXNEIGHBORS];
11264       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11265       for (int n = 0; n < nbNeighbors; n++)
11266       {
11267         int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
11268         const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11269         if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
11270         {
11271           bool ok = false;
11272           for ( size_t idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
11273           {
11274             // MESSAGE("Domain " << idombis);
11275             const TIDSortedElemSet& domainbis = theElems[idombis];
11276             if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11277           }
11278           if ( ok || onAllBoundaries ) // the characteristics of the face is stored
11279           {
11280             DownIdType face(downIds[n], downTypes[n]);
11281             if (!faceDomains[face].count(idom))
11282             {
11283               faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11284               celldom[vtkId] = idom;
11285               //MESSAGE("       cell with a border " << vtkId << " domain " << idom);
11286             }
11287             if ( !ok )
11288             {
11289               theRestDomElems.insert( elem );
11290               faceDomains[face][iRestDom] = neighborsVtkIds[n];
11291               celldom[neighborsVtkIds[n]] = iRestDom;
11292             }
11293           }
11294         }
11295       }
11296     }
11297   }
11298
11299   //MESSAGE("Number of shared faces " << faceDomains.size());
11300   std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11301
11302   // --- explore the shared faces domain by domain,
11303   //     explore the nodes of the face and see if they belong to a cell in the domain,
11304   //     which has only a node or an edge on the border (not a shared face)
11305
11306   for (int idomain = idom0; idomain < nbDomains; idomain++)
11307   {
11308     //MESSAGE("Domain " << idomain);
11309     const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
11310     itface = faceDomains.begin();
11311     for (; itface != faceDomains.end(); ++itface)
11312     {
11313       const std::map<int, int>& domvol = itface->second;
11314       if (!domvol.count(idomain))
11315         continue;
11316       DownIdType face = itface->first;
11317       //MESSAGE(" --- face " << face.cellId);
11318       std::set<int> oldNodes;
11319       oldNodes.clear();
11320       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11321       std::set<int>::iterator itn = oldNodes.begin();
11322       for (; itn != oldNodes.end(); ++itn)
11323       {
11324         int oldId = *itn;
11325         //MESSAGE("     node " << oldId);
11326         vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11327         for (int i=0; i<l.ncells; i++)
11328         {
11329           int vtkId = l.cells[i];
11330           const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
11331           if (!domain.count(anElem))
11332             continue;
11333           int vtkType = grid->GetCellType(vtkId);
11334           int downId = grid->CellIdToDownId(vtkId);
11335           if (downId < 0)
11336           {
11337             MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11338             continue; // not OK at this stage of the algorithm:
11339             //no cells created after BuildDownWardConnectivity
11340           }
11341           DownIdType aCell(downId, vtkType);
11342           cellDomains[aCell][idomain] = vtkId;
11343           celldom[vtkId] = idomain;
11344           //MESSAGE("       cell " << vtkId << " domain " << idomain);
11345         }
11346       }
11347     }
11348   }
11349
11350   // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11351   //     for each shared face, get the nodes
11352   //     for each node, for each domain of the face, create a clone of the node
11353
11354   // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11355   //     junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11356   //     the value is the ordered domain ids. (more than 4 domains not taken into account)
11357
11358   std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11359   std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11360   std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11361
11362   //MESSAGE(".. Duplication of the nodes");
11363   for (int idomain = idom0; idomain < nbDomains; idomain++)
11364   {
11365     itface = faceDomains.begin();
11366     for (; itface != faceDomains.end(); ++itface)
11367     {
11368       const std::map<int, int>& domvol = itface->second;
11369       if (!domvol.count(idomain))
11370         continue;
11371       DownIdType face = itface->first;
11372       //MESSAGE(" --- face " << face.cellId);
11373       std::set<int> oldNodes;
11374       oldNodes.clear();
11375       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11376       std::set<int>::iterator itn = oldNodes.begin();
11377       for (; itn != oldNodes.end(); ++itn)
11378       {
11379         int oldId = *itn;
11380         if (nodeDomains[oldId].empty())
11381         {
11382           nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11383           //MESSAGE("-+-+-b     oldNode " << oldId << " domain " << idomain);
11384         }
11385         std::map<int, int>::const_iterator itdom = domvol.begin();
11386         for (; itdom != domvol.end(); ++itdom)
11387         {
11388           int idom = itdom->first;
11389           //MESSAGE("         domain " << idom);
11390           if (!nodeDomains[oldId].count(idom)) // --- node to clone
11391           {
11392             if (nodeDomains[oldId].size() >= 2) // a multiple node
11393             {
11394               vector<int> orderedDoms;
11395               //MESSAGE("multiple node " << oldId);
11396               if (mutipleNodes.count(oldId))
11397                 orderedDoms = mutipleNodes[oldId];
11398               else
11399               {
11400                 map<int,int>::iterator it = nodeDomains[oldId].begin();
11401                 for (; it != nodeDomains[oldId].end(); ++it)
11402                   orderedDoms.push_back(it->first);
11403               }
11404               orderedDoms.push_back(idom); // TODO order ==> push_front or back
11405               //stringstream txt;
11406               //for (int i=0; i<orderedDoms.size(); i++)
11407               //  txt << orderedDoms[i] << " ";
11408               //MESSAGE("orderedDoms " << txt.str());
11409               mutipleNodes[oldId] = orderedDoms;
11410             }
11411             double *coords = grid->GetPoint(oldId);
11412             SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11413             copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11414             int newId = newNode->getVtkId();
11415             nodeDomains[oldId][idom] = newId; // cloned node for other domains
11416             //MESSAGE("-+-+-c     oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11417           }
11418         }
11419       }
11420     }
11421   }
11422
11423   //MESSAGE(".. Creation of elements");
11424   for (int idomain = idom0; idomain < nbDomains; idomain++)
11425   {
11426     itface = faceDomains.begin();
11427     for (; itface != faceDomains.end(); ++itface)
11428     {
11429       std::map<int, int> domvol = itface->second;
11430       if (!domvol.count(idomain))
11431         continue;
11432       DownIdType face = itface->first;
11433       //MESSAGE(" --- face " << face.cellId);
11434       std::set<int> oldNodes;
11435       oldNodes.clear();
11436       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11437       int nbMultipleNodes = 0;
11438       std::set<int>::iterator itn = oldNodes.begin();
11439       for (; itn != oldNodes.end(); ++itn)
11440       {
11441         int oldId = *itn;
11442         if (mutipleNodes.count(oldId))
11443           nbMultipleNodes++;
11444       }
11445       if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11446       {
11447         //MESSAGE("multiple Nodes detected on a shared face");
11448         int downId = itface->first.cellId;
11449         unsigned char cellType = itface->first.cellType;
11450         // --- shared edge or shared face ?
11451         if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11452         {
11453           int nodes[3];
11454           int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11455           for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11456             if (mutipleNodes.count(nodes[i]))
11457               if (!mutipleNodesToFace.count(nodes[i]))
11458                 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11459         }
11460         else // shared face (between two volumes)
11461         {
11462           int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11463           const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11464           const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11465           for (int ie =0; ie < nbEdges; ie++)
11466           {
11467             int nodes[3];
11468             int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11469             if ( mutipleNodes.count(nodes[0]) && mutipleNodes.count( nodes[ nbNodes-1 ]))
11470             {
11471               vector<int> vn0 = mutipleNodes[nodes[0]];
11472               vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11473               vector<int> doms;
11474               for ( size_t i0 = 0; i0 < vn0.size(); i0++ )
11475                 for ( size_t i1 = 0; i1 < vn1.size(); i1++ )
11476                   if ( vn0[i0] == vn1[i1] )
11477                     doms.push_back( vn0[ i0 ]);
11478               if ( doms.size() > 2 )
11479               {
11480                 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11481                 double *coords = grid->GetPoint(nodes[0]);
11482                 gp_Pnt p0(coords[0], coords[1], coords[2]);
11483                 coords = grid->GetPoint(nodes[nbNodes - 1]);
11484                 gp_Pnt p1(coords[0], coords[1], coords[2]);
11485                 gp_Pnt gref;
11486                 int vtkVolIds[1000];  // an edge can belong to a lot of volumes
11487                 map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
11488                 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11489                 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11490                 for ( size_t id = 0; id < doms.size(); id++ )
11491                 {
11492                   int idom = doms[id];
11493                   const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11494                   for ( int ivol = 0; ivol < nbvol; ivol++ )
11495                   {
11496                     int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
11497                     SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
11498                     if (domain.count(elem))
11499                     {
11500                       SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
11501                       domvol[idom] = svol;
11502                       //MESSAGE("  domain " << idom << " volume " << elem->GetID());
11503                       double values[3];
11504                       vtkIdType npts = 0;
11505                       vtkIdType* pts = 0;
11506                       grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11507                       SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
11508                       if (id ==0)
11509                       {
11510                         gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
11511                         angleDom[idom] = 0;
11512                       }
11513                       else
11514                       {
11515                         gp_Pnt g(values[0], values[1], values[2]);
11516                         angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11517                         //MESSAGE("  angle=" << angleDom[idom]);
11518                       }
11519                       break;
11520                     }
11521                   }
11522                 }
11523                 map<double, int> sortedDom; // sort domains by angle
11524                 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11525                   sortedDom[ia->second] = ia->first;
11526                 vector<int> vnodes;
11527                 vector<int> vdom;
11528                 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11529                 {
11530                   vdom.push_back(ib->second);
11531                   //MESSAGE("  ordered domain " << ib->second << "  angle " << ib->first);
11532                 }
11533                 for (int ino = 0; ino < nbNodes; ino++)
11534                   vnodes.push_back(nodes[ino]);
11535                 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11536               }
11537             }
11538           }
11539         }
11540       }
11541     }
11542   }
11543
11544   // --- iterate on shared faces (volumes to modify, face to extrude)
11545   //     get node id's of the face (id SMDS = id VTK)
11546   //     create flat element with old and new nodes if requested
11547
11548   // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11549   //     (domain1 X domain2) = domain1 + MAXINT*domain2
11550
11551   std::map<int, std::map<long,int> > nodeQuadDomains;
11552   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11553
11554   //MESSAGE(".. Creation of elements: simple junction");
11555   if (createJointElems)
11556   {
11557     int idg;
11558     string joints2DName = "joints2D";
11559     mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
11560     SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11561     string joints3DName = "joints3D";
11562     mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
11563     SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11564
11565     itface = faceDomains.begin();
11566     for (; itface != faceDomains.end(); ++itface)
11567     {
11568       DownIdType face = itface->first;
11569       std::set<int> oldNodes;
11570       std::set<int>::iterator itn;
11571       oldNodes.clear();
11572       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11573
11574       std::map<int, int> domvol = itface->second;
11575       std::map<int, int>::iterator itdom = domvol.begin();
11576       int dom1 = itdom->first;
11577       int vtkVolId = itdom->second;
11578       itdom++;
11579       int dom2 = itdom->first;
11580       SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11581                                                        nodeQuadDomains);
11582       stringstream grpname;
11583       grpname << "j_";
11584       if (dom1 < dom2)
11585         grpname << dom1 << "_" << dom2;
11586       else
11587         grpname << dom2 << "_" << dom1;
11588       string namegrp = grpname.str();
11589       if (!mapOfJunctionGroups.count(namegrp))
11590         mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
11591       SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11592       if (sgrp)
11593         sgrp->Add(vol->GetID());
11594       if (vol->GetType() == SMDSAbs_Volume)
11595         joints3DGrp->Add(vol->GetID());
11596       else if (vol->GetType() == SMDSAbs_Face)
11597         joints2DGrp->Add(vol->GetID());
11598     }
11599   }
11600
11601   // --- create volumes on multiple domain intersection if requested
11602   //     iterate on mutipleNodesToFace
11603   //     iterate on edgesMultiDomains
11604
11605   //MESSAGE(".. Creation of elements: multiple junction");
11606   if (createJointElems)
11607   {
11608     // --- iterate on mutipleNodesToFace
11609
11610     std::map<int, std::vector<int> >::iterator itn =  mutipleNodesToFace.begin();
11611     for (; itn != mutipleNodesToFace.end(); ++itn)
11612     {
11613       int node = itn->first;
11614       vector<int> orderDom = itn->second;
11615       vector<vtkIdType> orderedNodes;
11616       for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11617         orderedNodes.push_back( nodeDomains[ node ][ orderDom[ idom ]]);
11618       SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11619
11620       stringstream grpname;
11621       grpname << "m2j_";
11622       grpname << 0 << "_" << 0;
11623       int idg;
11624       string namegrp = grpname.str();
11625       if (!mapOfJunctionGroups.count(namegrp))
11626         mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
11627       SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11628       if (sgrp)
11629         sgrp->Add(face->GetID());
11630     }
11631
11632     // --- iterate on edgesMultiDomains
11633
11634     std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11635     for (; ite != edgesMultiDomains.end(); ++ite)
11636     {
11637       vector<int> nodes = ite->first;
11638       vector<int> orderDom = ite->second;
11639       vector<vtkIdType> orderedNodes;
11640       if (nodes.size() == 2)
11641       {
11642         //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11643         for ( size_t ino = 0; ino < nodes.size(); ino++ )
11644           if ( orderDom.size() == 3 )
11645             for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11646               orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11647           else
11648             for (int idom = orderDom.size()-1; idom >=0; idom--)
11649               orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11650         SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11651
11652         int idg;
11653         string namegrp = "jointsMultiples";
11654         if (!mapOfJunctionGroups.count(namegrp))
11655           mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11656         SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11657         if (sgrp)
11658           sgrp->Add(vol->GetID());
11659       }
11660       else
11661       {
11662         //INFOS("Quadratic multiple joints not implemented");
11663         // TODO quadratic nodes
11664       }
11665     }
11666   }
11667
11668   // --- list the explicit faces and edges of the mesh that need to be modified,
11669   //     i.e. faces and edges built with one or more duplicated nodes.
11670   //     associate these faces or edges to their corresponding domain.
11671   //     only the first domain found is kept when a face or edge is shared
11672
11673   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11674   std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11675   faceOrEdgeDom.clear();
11676   feDom.clear();
11677
11678   //MESSAGE(".. Modification of elements");
11679   for (int idomain = idom0; idomain < nbDomains; idomain++)
11680   {
11681     std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11682     for (; itnod != nodeDomains.end(); ++itnod)
11683     {
11684       int oldId = itnod->first;
11685       //MESSAGE("     node " << oldId);
11686       vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11687       for (int i = 0; i < l.ncells; i++)
11688       {
11689         int vtkId = l.cells[i];
11690         int vtkType = grid->GetCellType(vtkId);
11691         int downId = grid->CellIdToDownId(vtkId);
11692         if (downId < 0)
11693           continue; // new cells: not to be modified
11694         DownIdType aCell(downId, vtkType);
11695         int volParents[1000];
11696         int nbvol = grid->GetParentVolumes(volParents, vtkId);
11697         for (int j = 0; j < nbvol; j++)
11698           if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11699             if (!feDom.count(vtkId))
11700             {
11701               feDom[vtkId] = idomain;
11702               faceOrEdgeDom[aCell] = emptyMap;
11703               faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11704               //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
11705               //        << " type " << vtkType << " downId " << downId);
11706             }
11707       }
11708     }
11709   }
11710
11711   // --- iterate on shared faces (volumes to modify, face to extrude)
11712   //     get node id's of the face
11713   //     replace old nodes by new nodes in volumes, and update inverse connectivity
11714
11715   std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11716   for (int m=0; m<3; m++)
11717   {
11718     std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11719     itface = (*amap).begin();
11720     for (; itface != (*amap).end(); ++itface)
11721     {
11722       DownIdType face = itface->first;
11723       std::set<int> oldNodes;
11724       std::set<int>::iterator itn;
11725       oldNodes.clear();
11726       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11727       //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11728       std::map<int, int> localClonedNodeIds;
11729
11730       std::map<int, int> domvol = itface->second;
11731       std::map<int, int>::iterator itdom = domvol.begin();
11732       for (; itdom != domvol.end(); ++itdom)
11733       {
11734         int idom = itdom->first;
11735         int vtkVolId = itdom->second;
11736         //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
11737         localClonedNodeIds.clear();
11738         for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11739         {
11740           int oldId = *itn;
11741           if (nodeDomains[oldId].count(idom))
11742           {
11743             localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11744             //MESSAGE("     node " << oldId << " --> " << localClonedNodeIds[oldId]);
11745           }
11746         }
11747         meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11748       }
11749     }
11750   }
11751
11752   // Remove empty groups (issue 0022812)
11753   std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
11754   for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
11755   {
11756     if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
11757       myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
11758   }
11759
11760   meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11761   grid->DeleteLinks();
11762
11763   CHRONOSTOP(50);
11764   counters::stats();
11765   return true;
11766 }
11767
11768 /*!
11769  * \brief Double nodes on some external faces and create flat elements.
11770  * Flat elements are mainly used by some types of mechanic calculations.
11771  *
11772  * Each group of the list must be constituted of faces.
11773  * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11774  * @param theElems - list of groups of faces, where a group of faces is a set of
11775  * SMDS_MeshElements sorted by Id.
11776  * @return TRUE if operation has been completed successfully, FALSE otherwise
11777  */
11778 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11779 {
11780   // MESSAGE("-------------------------------------------------");
11781   // MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11782   // MESSAGE("-------------------------------------------------");
11783
11784   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11785
11786   // --- For each group of faces
11787   //     duplicate the nodes, create a flat element based on the face
11788   //     replace the nodes of the faces by their clones
11789
11790   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11791   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11792   clonedNodes.clear();
11793   intermediateNodes.clear();
11794   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11795   mapOfJunctionGroups.clear();
11796
11797   for ( size_t idom = 0; idom < theElems.size(); idom++ )
11798   {
11799     const TIDSortedElemSet&           domain = theElems[idom];
11800     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11801     for ( ; elemItr != domain.end(); ++elemItr )
11802     {
11803       SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11804       SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
11805       if (!aFace)
11806         continue;
11807       // MESSAGE("aFace=" << aFace->GetID());
11808       bool isQuad = aFace->IsQuadratic();
11809       vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11810
11811       // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11812
11813       SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
11814       while (nodeIt->more())
11815       {
11816         const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
11817         bool isMedium = isQuad && (aFace->IsMediumNode(node));
11818         if (isMedium)
11819           ln2.push_back(node);
11820         else
11821           ln0.push_back(node);
11822
11823         const SMDS_MeshNode* clone = 0;
11824         if (!clonedNodes.count(node))
11825         {
11826           clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11827           copyPosition( node, clone );
11828           clonedNodes[node] = clone;
11829         }
11830         else
11831           clone = clonedNodes[node];
11832
11833         if (isMedium)
11834           ln3.push_back(clone);
11835         else
11836           ln1.push_back(clone);
11837
11838         const SMDS_MeshNode* inter = 0;
11839         if (isQuad && (!isMedium))
11840         {
11841           if (!intermediateNodes.count(node))
11842           {
11843             inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11844             copyPosition( node, inter );
11845             intermediateNodes[node] = inter;
11846           }
11847           else
11848             inter = intermediateNodes[node];
11849           ln4.push_back(inter);
11850         }
11851       }
11852
11853       // --- extrude the face
11854
11855       vector<const SMDS_MeshNode*> ln;
11856       SMDS_MeshVolume* vol = 0;
11857       vtkIdType aType = aFace->GetVtkType();
11858       switch (aType)
11859       {
11860       case VTK_TRIANGLE:
11861         vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11862         // MESSAGE("vol prism " << vol->GetID());
11863         ln.push_back(ln1[0]);
11864         ln.push_back(ln1[1]);
11865         ln.push_back(ln1[2]);
11866         break;
11867       case VTK_QUAD:
11868         vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11869         // MESSAGE("vol hexa " << vol->GetID());
11870         ln.push_back(ln1[0]);
11871         ln.push_back(ln1[1]);
11872         ln.push_back(ln1[2]);
11873         ln.push_back(ln1[3]);
11874         break;
11875       case VTK_QUADRATIC_TRIANGLE:
11876         vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11877                                 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11878         // MESSAGE("vol quad prism " << vol->GetID());
11879         ln.push_back(ln1[0]);
11880         ln.push_back(ln1[1]);
11881         ln.push_back(ln1[2]);
11882         ln.push_back(ln3[0]);
11883         ln.push_back(ln3[1]);
11884         ln.push_back(ln3[2]);
11885         break;
11886       case VTK_QUADRATIC_QUAD:
11887         //              vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11888         //                                      ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11889         //                                      ln4[0], ln4[1], ln4[2], ln4[3]);
11890         vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11891                                 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11892                                 ln4[0], ln4[1], ln4[2], ln4[3]);
11893         // MESSAGE("vol quad hexa " << vol->GetID());
11894         ln.push_back(ln1[0]);
11895         ln.push_back(ln1[1]);
11896         ln.push_back(ln1[2]);
11897         ln.push_back(ln1[3]);
11898         ln.push_back(ln3[0]);
11899         ln.push_back(ln3[1]);
11900         ln.push_back(ln3[2]);
11901         ln.push_back(ln3[3]);
11902         break;
11903       case VTK_POLYGON:
11904         break;
11905       default:
11906         break;
11907       }
11908
11909       if (vol)
11910       {
11911         stringstream grpname;
11912         grpname << "jf_";
11913         grpname << idom;
11914         int idg;
11915         string namegrp = grpname.str();
11916         if (!mapOfJunctionGroups.count(namegrp))
11917           mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11918         SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11919         if (sgrp)
11920           sgrp->Add(vol->GetID());
11921       }
11922
11923       // --- modify the face
11924
11925       aFace->ChangeNodes(&ln[0], ln.size());
11926     }
11927   }
11928   return true;
11929 }
11930
11931 /*!
11932  *  \brief identify all the elements around a geom shape, get the faces delimiting the hole
11933  *  Build groups of volume to remove, groups of faces to replace on the skin of the object,
11934  *  groups of faces to remove inside the object, (idem edges).
11935  *  Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11936  */
11937 void SMESH_MeshEditor::CreateHoleSkin(double                          radius,
11938                                       const TopoDS_Shape&             theShape,
11939                                       SMESH_NodeSearcher*             theNodeSearcher,
11940                                       const char*                     groupName,
11941                                       std::vector<double>&            nodesCoords,
11942                                       std::vector<std::vector<int> >& listOfListOfNodes)
11943 {
11944   // MESSAGE("--------------------------------");
11945   // MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11946   // MESSAGE("--------------------------------");
11947
11948   // --- zone of volumes to remove is given :
11949   //     1 either by a geom shape (one or more vertices) and a radius,
11950   //     2 either by a group of nodes (representative of the shape)to use with the radius,
11951   //     3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11952   //     In the case 2, the group of nodes is an external group of nodes from another mesh,
11953   //     In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11954   //     defined by it's name.
11955
11956   SMESHDS_GroupBase* groupDS = 0;
11957   SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11958   while ( groupIt->more() )
11959   {
11960     groupDS = 0;
11961     SMESH_Group * group = groupIt->next();
11962     if ( !group ) continue;
11963     groupDS = group->GetGroupDS();
11964     if ( !groupDS || groupDS->IsEmpty() ) continue;
11965     std::string grpName = group->GetName();
11966     //MESSAGE("grpName=" << grpName);
11967     if (grpName == groupName)
11968       break;
11969     else
11970       groupDS = 0;
11971   }
11972
11973   bool isNodeGroup = false;
11974   bool isNodeCoords = false;
11975   if (groupDS)
11976   {
11977     if (groupDS->GetType() != SMDSAbs_Node)
11978       return;
11979     isNodeGroup = true;     // a group of nodes exists and it is in this mesh
11980   }
11981
11982   if (nodesCoords.size() > 0)
11983     isNodeCoords = true; // a list o nodes given by their coordinates
11984   //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11985
11986   // --- define groups to build
11987
11988   int idg; // --- group of SMDS volumes
11989   string grpvName = groupName;
11990   grpvName += "_vol";
11991   SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
11992   if (!grp)
11993   {
11994     MESSAGE("group not created " << grpvName);
11995     return;
11996   }
11997   SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
11998
11999   int idgs; // --- group of SMDS faces on the skin
12000   string grpsName = groupName;
12001   grpsName += "_skin";
12002   SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
12003   if (!grps)
12004   {
12005     MESSAGE("group not created " << grpsName);
12006     return;
12007   }
12008   SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
12009
12010   int idgi; // --- group of SMDS faces internal (several shapes)
12011   string grpiName = groupName;
12012   grpiName += "_internalFaces";
12013   SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
12014   if (!grpi)
12015   {
12016     MESSAGE("group not created " << grpiName);
12017     return;
12018   }
12019   SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
12020
12021   int idgei; // --- group of SMDS faces internal (several shapes)
12022   string grpeiName = groupName;
12023   grpeiName += "_internalEdges";
12024   SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
12025   if (!grpei)
12026   {
12027     MESSAGE("group not created " << grpeiName);
12028     return;
12029   }
12030   SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
12031
12032   // --- build downward connectivity
12033
12034   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
12035   meshDS->BuildDownWardConnectivity(true);
12036   SMDS_UnstructuredGrid* grid = meshDS->getGrid();
12037
12038   // --- set of volumes detected inside
12039
12040   std::set<int> setOfInsideVol;
12041   std::set<int> setOfVolToCheck;
12042
12043   std::vector<gp_Pnt> gpnts;
12044   gpnts.clear();
12045
12046   if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
12047   {
12048     //MESSAGE("group of nodes provided");
12049     SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
12050     while ( elemIt->more() )
12051     {
12052       const SMDS_MeshElement* elem = elemIt->next();
12053       if (!elem)
12054         continue;
12055       const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
12056       if (!node)
12057         continue;
12058       SMDS_MeshElement* vol = 0;
12059       SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
12060       while (volItr->more())
12061       {
12062         vol = (SMDS_MeshElement*)volItr->next();
12063         setOfInsideVol.insert(vol->getVtkId());
12064         sgrp->Add(vol->GetID());
12065       }
12066     }
12067   }
12068   else if (isNodeCoords)
12069   {
12070     //MESSAGE("list of nodes coordinates provided");
12071     size_t i = 0;
12072     int k = 0;
12073     while ( i < nodesCoords.size()-2 )
12074     {
12075       double x = nodesCoords[i++];
12076       double y = nodesCoords[i++];
12077       double z = nodesCoords[i++];
12078       gp_Pnt p = gp_Pnt(x, y ,z);
12079       gpnts.push_back(p);
12080       //MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
12081       k++;
12082     }
12083   }
12084   else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
12085   {
12086     //MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
12087     TopTools_IndexedMapOfShape vertexMap;
12088     TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
12089     gp_Pnt p = gp_Pnt(0,0,0);
12090     if (vertexMap.Extent() < 1)
12091       return;
12092
12093     for ( int i = 1; i <= vertexMap.Extent(); ++i )
12094     {
12095       const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
12096       p = BRep_Tool::Pnt(vertex);
12097       gpnts.push_back(p);
12098       //MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
12099     }
12100   }
12101
12102   if (gpnts.size() > 0)
12103   {
12104     const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
12105     //MESSAGE("startNode->nodeId " << nodeId);
12106
12107     double radius2 = radius*radius;
12108     //MESSAGE("radius2 " << radius2);
12109
12110     // --- volumes on start node
12111
12112     setOfVolToCheck.clear();
12113     SMDS_MeshElement* startVol = 0;
12114     SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
12115     while (volItr->more())
12116     {
12117       startVol = (SMDS_MeshElement*)volItr->next();
12118       setOfVolToCheck.insert(startVol->getVtkId());
12119     }
12120     if (setOfVolToCheck.empty())
12121     {
12122       MESSAGE("No volumes found");
12123       return;
12124     }
12125
12126     // --- starting with central volumes then their neighbors, check if they are inside
12127     //     or outside the domain, until no more new neighbor volume is inside.
12128     //     Fill the group of inside volumes
12129
12130     std::map<int, double> mapOfNodeDistance2;
12131     mapOfNodeDistance2.clear();
12132     std::set<int> setOfOutsideVol;
12133     while (!setOfVolToCheck.empty())
12134     {
12135       std::set<int>::iterator it = setOfVolToCheck.begin();
12136       int vtkId = *it;
12137       //MESSAGE("volume to check,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12138       bool volInside = false;
12139       vtkIdType npts = 0;
12140       vtkIdType* pts = 0;
12141       grid->GetCellPoints(vtkId, npts, pts);
12142       for (int i=0; i<npts; i++)
12143       {
12144         double distance2 = 0;
12145         if (mapOfNodeDistance2.count(pts[i]))
12146         {
12147           distance2 = mapOfNodeDistance2[pts[i]];
12148           //MESSAGE("point " << pts[i] << " distance2 " << distance2);
12149         }
12150         else
12151         {
12152           double *coords = grid->GetPoint(pts[i]);
12153           gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
12154           distance2 = 1.E40;
12155           for ( size_t j = 0; j < gpnts.size(); j++ )
12156           {
12157             double d2 = aPoint.SquareDistance( gpnts[ j ]);
12158             if (d2 < distance2)
12159             {
12160               distance2 = d2;
12161               if (distance2 < radius2)
12162                 break;
12163             }
12164           }
12165           mapOfNodeDistance2[pts[i]] = distance2;
12166           //MESSAGE("  point "  << pts[i]  << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " <<  coords[2]);
12167         }
12168         if (distance2 < radius2)
12169         {
12170           volInside = true; // one or more nodes inside the domain
12171           sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12172           break;
12173         }
12174       }
12175       if (volInside)
12176       {
12177         setOfInsideVol.insert(vtkId);
12178         //MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12179         int neighborsVtkIds[NBMAXNEIGHBORS];
12180         int downIds[NBMAXNEIGHBORS];
12181         unsigned char downTypes[NBMAXNEIGHBORS];
12182         int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12183         for (int n = 0; n < nbNeighbors; n++)
12184           if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12185             setOfVolToCheck.insert(neighborsVtkIds[n]);
12186       }
12187       else
12188       {
12189         setOfOutsideVol.insert(vtkId);
12190         //MESSAGE("  volume outside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12191       }
12192       setOfVolToCheck.erase(vtkId);
12193     }
12194   }
12195
12196   // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12197   //     If yes, add the volume to the inside set
12198
12199   bool addedInside = true;
12200   std::set<int> setOfVolToReCheck;
12201   while (addedInside)
12202   {
12203     //MESSAGE(" --------------------------- re check");
12204     addedInside = false;
12205     std::set<int>::iterator itv = setOfInsideVol.begin();
12206     for (; itv != setOfInsideVol.end(); ++itv)
12207     {
12208       int vtkId = *itv;
12209       int neighborsVtkIds[NBMAXNEIGHBORS];
12210       int downIds[NBMAXNEIGHBORS];
12211       unsigned char downTypes[NBMAXNEIGHBORS];
12212       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12213       for (int n = 0; n < nbNeighbors; n++)
12214         if (!setOfInsideVol.count(neighborsVtkIds[n]))
12215           setOfVolToReCheck.insert(neighborsVtkIds[n]);
12216     }
12217     setOfVolToCheck = setOfVolToReCheck;
12218     setOfVolToReCheck.clear();
12219     while  (!setOfVolToCheck.empty())
12220     {
12221       std::set<int>::iterator it = setOfVolToCheck.begin();
12222       int vtkId = *it;
12223       if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12224       {
12225         //MESSAGE("volume to recheck,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12226         int countInside = 0;
12227         int neighborsVtkIds[NBMAXNEIGHBORS];
12228         int downIds[NBMAXNEIGHBORS];
12229         unsigned char downTypes[NBMAXNEIGHBORS];
12230         int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12231         for (int n = 0; n < nbNeighbors; n++)
12232           if (setOfInsideVol.count(neighborsVtkIds[n]))
12233             countInside++;
12234         //MESSAGE("countInside " << countInside);
12235         if (countInside > 1)
12236         {
12237           //MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12238           setOfInsideVol.insert(vtkId);
12239           sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12240           addedInside = true;
12241         }
12242         else
12243           setOfVolToReCheck.insert(vtkId);
12244       }
12245       setOfVolToCheck.erase(vtkId);
12246     }
12247   }
12248
12249   // --- map of Downward faces at the boundary, inside the global volume
12250   //     map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12251   //     fill group of SMDS faces inside the volume (when several volume shapes)
12252   //     fill group of SMDS faces on the skin of the global volume (if skin)
12253
12254   std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12255   std::map<DownIdType, int, DownIdCompare> skinFaces;     // faces on the skin of the global volume --> corresponding cell
12256   std::set<int>::iterator it = setOfInsideVol.begin();
12257   for (; it != setOfInsideVol.end(); ++it)
12258   {
12259     int vtkId = *it;
12260     //MESSAGE("  vtkId " << vtkId  << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12261     int neighborsVtkIds[NBMAXNEIGHBORS];
12262     int downIds[NBMAXNEIGHBORS];
12263     unsigned char downTypes[NBMAXNEIGHBORS];
12264     int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12265     for (int n = 0; n < nbNeighbors; n++)
12266     {
12267       int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12268       if (neighborDim == 3)
12269       {
12270         if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12271         {
12272           DownIdType face(downIds[n], downTypes[n]);
12273           boundaryFaces[face] = vtkId;
12274         }
12275         // if the face between to volumes is in the mesh, get it (internal face between shapes)
12276         int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12277         if (vtkFaceId >= 0)
12278         {
12279           sgrpi->Add(meshDS->fromVtkToSmds(vtkFaceId));
12280           // find also the smds edges on this face
12281           int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12282           const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12283           const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12284           for (int i = 0; i < nbEdges; i++)
12285           {
12286             int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12287             if (vtkEdgeId >= 0)
12288               sgrpei->Add(meshDS->fromVtkToSmds(vtkEdgeId));
12289           }
12290         }
12291       }
12292       else if (neighborDim == 2) // skin of the volume
12293       {
12294         DownIdType face(downIds[n], downTypes[n]);
12295         skinFaces[face] = vtkId;
12296         int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12297         if (vtkFaceId >= 0)
12298           sgrps->Add(meshDS->fromVtkToSmds(vtkFaceId));
12299       }
12300     }
12301   }
12302
12303   // --- identify the edges constituting the wire of each subshape on the skin
12304   //     define polylines with the nodes of edges, equivalent to wires
12305   //     project polylines on subshapes, and partition, to get geom faces
12306
12307   std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12308   std::set<int> emptySet;
12309   emptySet.clear();
12310   std::set<int> shapeIds;
12311
12312   SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12313   while (itelem->more())
12314   {
12315     const SMDS_MeshElement *elem = itelem->next();
12316     int shapeId = elem->getshapeId();
12317     int vtkId = elem->getVtkId();
12318     if (!shapeIdToVtkIdSet.count(shapeId))
12319     {
12320       shapeIdToVtkIdSet[shapeId] = emptySet;
12321       shapeIds.insert(shapeId);
12322     }
12323     shapeIdToVtkIdSet[shapeId].insert(vtkId);
12324   }
12325
12326   std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12327   std::set<DownIdType, DownIdCompare> emptyEdges;
12328   emptyEdges.clear();
12329
12330   std::map<int, std::set<int> >::iterator itShape =  shapeIdToVtkIdSet.begin();
12331   for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12332   {
12333     int shapeId = itShape->first;
12334     //MESSAGE(" --- Shape ID --- "<< shapeId);
12335     shapeIdToEdges[shapeId] = emptyEdges;
12336
12337     std::vector<int> nodesEdges;
12338
12339     std::set<int>::iterator its = itShape->second.begin();
12340     for (; its != itShape->second.end(); ++its)
12341     {
12342       int vtkId = *its;
12343       //MESSAGE("     " << vtkId);
12344       int neighborsVtkIds[NBMAXNEIGHBORS];
12345       int downIds[NBMAXNEIGHBORS];
12346       unsigned char downTypes[NBMAXNEIGHBORS];
12347       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12348       for (int n = 0; n < nbNeighbors; n++)
12349       {
12350         if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12351           continue;
12352         int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
12353         const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12354         if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12355         {
12356           DownIdType edge(downIds[n], downTypes[n]);
12357           if (!shapeIdToEdges[shapeId].count(edge))
12358           {
12359             shapeIdToEdges[shapeId].insert(edge);
12360             int vtkNodeId[3];
12361             int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12362             nodesEdges.push_back(vtkNodeId[0]);
12363             nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12364             //MESSAGE("       --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12365           }
12366         }
12367       }
12368     }
12369
12370     std::list<int> order;
12371     order.clear();
12372     if (nodesEdges.size() > 0)
12373     {
12374       order.push_back(nodesEdges[0]); //MESSAGE("       --- back " << order.back()+1); // SMDS id = VTK id + 1;
12375       nodesEdges[0] = -1;
12376       order.push_back(nodesEdges[1]); //MESSAGE("       --- back " << order.back()+1);
12377       nodesEdges[1] = -1; // do not reuse this edge
12378       bool found = true;
12379       while (found)
12380       {
12381         int nodeTofind = order.back(); // try first to push back
12382         int i = 0;
12383         for ( i = 0; i < (int)nodesEdges.size(); i++ )
12384           if (nodesEdges[i] == nodeTofind)
12385             break;
12386         if ( i == (int) nodesEdges.size() )
12387           found = false; // no follower found on back
12388         else
12389         {
12390           if (i%2) // odd ==> use the previous one
12391             if (nodesEdges[i-1] < 0)
12392               found = false;
12393             else
12394             {
12395               order.push_back(nodesEdges[i-1]); //MESSAGE("       --- back " << order.back()+1);
12396               nodesEdges[i-1] = -1;
12397             }
12398           else // even ==> use the next one
12399             if (nodesEdges[i+1] < 0)
12400               found = false;
12401             else
12402             {
12403               order.push_back(nodesEdges[i+1]); //MESSAGE("       --- back " << order.back()+1);
12404               nodesEdges[i+1] = -1;
12405             }
12406         }
12407         if (found)
12408           continue;
12409         // try to push front
12410         found = true;
12411         nodeTofind = order.front(); // try to push front
12412         for ( i = 0;  i < (int)nodesEdges.size(); i++ )
12413           if ( nodesEdges[i] == nodeTofind )
12414             break;
12415         if ( i == (int)nodesEdges.size() )
12416         {
12417           found = false; // no predecessor found on front
12418           continue;
12419         }
12420         if (i%2) // odd ==> use the previous one
12421           if (nodesEdges[i-1] < 0)
12422             found = false;
12423           else
12424           {
12425             order.push_front(nodesEdges[i-1]); //MESSAGE("       --- front " << order.front()+1);
12426             nodesEdges[i-1] = -1;
12427           }
12428         else // even ==> use the next one
12429           if (nodesEdges[i+1] < 0)
12430             found = false;
12431           else
12432           {
12433             order.push_front(nodesEdges[i+1]); //MESSAGE("       --- front " << order.front()+1);
12434             nodesEdges[i+1] = -1;
12435           }
12436       }
12437     }
12438
12439
12440     std::vector<int> nodes;
12441     nodes.push_back(shapeId);
12442     std::list<int>::iterator itl = order.begin();
12443     for (; itl != order.end(); itl++)
12444     {
12445       nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12446       //MESSAGE("              ordered node " << nodes[nodes.size()-1]);
12447     }
12448     listOfListOfNodes.push_back(nodes);
12449   }
12450
12451   //     partition geom faces with blocFissure
12452   //     mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12453   //     mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12454
12455   return;
12456 }
12457
12458
12459 //================================================================================
12460 /*!
12461  * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12462  * The created 2D mesh elements based on nodes of free faces of boundary volumes
12463  * \return TRUE if operation has been completed successfully, FALSE otherwise
12464  */
12465 //================================================================================
12466
12467 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12468 {
12469   // iterates on volume elements and detect all free faces on them
12470   SMESHDS_Mesh* aMesh = GetMeshDS();
12471   if (!aMesh)
12472     return false;
12473
12474   ElemFeatures faceType( SMDSAbs_Face );
12475   int nbFree = 0, nbExisted = 0, nbCreated = 0;
12476   SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12477   while(vIt->more())
12478   {
12479     const SMDS_MeshVolume* volume = vIt->next();
12480     SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12481     vTool.SetExternalNormal();
12482     const int iQuad = volume->IsQuadratic();
12483     faceType.SetQuad( iQuad );
12484     for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12485     {
12486       if (!vTool.IsFreeFace(iface))
12487         continue;
12488       nbFree++;
12489       vector<const SMDS_MeshNode *> nodes;
12490       int nbFaceNodes = vTool.NbFaceNodes(iface);
12491       const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12492       int inode = 0;
12493       for ( ; inode < nbFaceNodes; inode += iQuad+1)
12494         nodes.push_back(faceNodes[inode]);
12495
12496       if (iQuad) // add medium nodes
12497       {
12498         for ( inode = 1; inode < nbFaceNodes; inode += 2)
12499           nodes.push_back(faceNodes[inode]);
12500         if ( nbFaceNodes == 9 ) // bi-quadratic quad
12501           nodes.push_back(faceNodes[8]);
12502       }
12503       // add new face based on volume nodes
12504       if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) )
12505       {
12506         nbExisted++; // face already exsist
12507       }
12508       else
12509       {
12510         AddElement( nodes, faceType.SetPoly( nbFaceNodes/(iQuad+1) > 4 ));
12511         nbCreated++;
12512       }
12513     }
12514   }
12515   return ( nbFree == ( nbExisted + nbCreated ));
12516 }
12517
12518 namespace
12519 {
12520   inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12521   {
12522     if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12523       return n;
12524     return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12525   }
12526 }
12527 //================================================================================
12528 /*!
12529  * \brief Creates missing boundary elements
12530  *  \param elements - elements whose boundary is to be checked
12531  *  \param dimension - defines type of boundary elements to create
12532  *  \param group - a group to store created boundary elements in
12533  *  \param targetMesh - a mesh to store created boundary elements in
12534  *  \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12535  *  \param toCopyExistingBoundary - if true, not only new but also pre-existing
12536  *                                boundary elements will be copied into the targetMesh
12537  *  \param toAddExistingBondary - if true, not only new but also pre-existing
12538  *                                boundary elements will be added into the new group
12539  *  \param aroundElements - if true, elements will be created on boundary of given
12540  *                          elements else, on boundary of the whole mesh.
12541  * \return nb of added boundary elements
12542  */
12543 //================================================================================
12544
12545 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12546                                        Bnd_Dimension           dimension,
12547                                        SMESH_Group*            group/*=0*/,
12548                                        SMESH_Mesh*             targetMesh/*=0*/,
12549                                        bool                    toCopyElements/*=false*/,
12550                                        bool                    toCopyExistingBoundary/*=false*/,
12551                                        bool                    toAddExistingBondary/*= false*/,
12552                                        bool                    aroundElements/*= false*/)
12553 {
12554   SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12555   SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12556   // hope that all elements are of the same type, do not check them all
12557   if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12558     throw SALOME_Exception(LOCALIZED("wrong element type"));
12559
12560   if ( !targetMesh )
12561     toCopyElements = toCopyExistingBoundary = false;
12562
12563   SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12564   SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12565   int nbAddedBnd = 0;
12566
12567   // editor adding present bnd elements and optionally holding elements to add to the group
12568   SMESH_MeshEditor* presentEditor;
12569   SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12570   presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12571
12572   SMESH_MesherHelper helper( *myMesh );
12573   const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12574   SMDS_VolumeTool vTool;
12575   TIDSortedElemSet avoidSet;
12576   const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12577   size_t inode;
12578
12579   typedef vector<const SMDS_MeshNode*> TConnectivity;
12580   TConnectivity tgtNodes;
12581   ElemFeatures elemKind( missType ), elemToCopy;
12582
12583   vector<const SMDS_MeshElement*> presentBndElems;
12584   vector<TConnectivity>           missingBndElems;
12585   vector<int>                     freeFacets;
12586   TConnectivity nodes, elemNodes;
12587
12588   SMDS_ElemIteratorPtr eIt;
12589   if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12590   else                  eIt = elemSetIterator( elements );
12591
12592   while (eIt->more())
12593   {
12594     const SMDS_MeshElement* elem = eIt->next();
12595     const int              iQuad = elem->IsQuadratic();
12596     elemKind.SetQuad( iQuad );
12597
12598     // ------------------------------------------------------------------------------------
12599     // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12600     // ------------------------------------------------------------------------------------
12601     presentBndElems.clear();
12602     missingBndElems.clear();
12603     freeFacets.clear(); nodes.clear(); elemNodes.clear();
12604     if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12605     {
12606       const SMDS_MeshElement* otherVol = 0;
12607       for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12608       {
12609         if ( !vTool.IsFreeFace(iface, &otherVol) &&
12610              ( !aroundElements || elements.count( otherVol )))
12611           continue;
12612         freeFacets.push_back( iface );
12613       }
12614       if ( missType == SMDSAbs_Face )
12615         vTool.SetExternalNormal();
12616       for ( size_t i = 0; i < freeFacets.size(); ++i )
12617       {
12618         int                iface = freeFacets[i];
12619         const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12620         const size_t nbFaceNodes = vTool.NbFaceNodes (iface);
12621         if ( missType == SMDSAbs_Edge ) // boundary edges
12622         {
12623           nodes.resize( 2+iQuad );
12624           for ( size_t i = 0; i < nbFaceNodes; i += 1+iQuad )
12625           {
12626             for ( size_t j = 0; j < nodes.size(); ++j )
12627               nodes[ j ] = nn[ i+j ];
12628             if ( const SMDS_MeshElement* edge =
12629                  aMesh->FindElement( nodes, SMDSAbs_Edge, /*noMedium=*/false ))
12630               presentBndElems.push_back( edge );
12631             else
12632               missingBndElems.push_back( nodes );
12633           }
12634         }
12635         else // boundary face
12636         {
12637           nodes.clear();
12638           for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12639             nodes.push_back( nn[inode] ); // add corner nodes
12640           if (iQuad)
12641             for ( inode = 1; inode < nbFaceNodes; inode += 2)
12642               nodes.push_back( nn[inode] ); // add medium nodes
12643           int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12644           if ( iCenter > 0 )
12645             nodes.push_back( vTool.GetNodes()[ iCenter ] );
12646
12647           if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12648                                                                SMDSAbs_Face, /*noMedium=*/false ))
12649             presentBndElems.push_back( f );
12650           else
12651             missingBndElems.push_back( nodes );
12652
12653           if ( targetMesh != myMesh )
12654           {
12655             // add 1D elements on face boundary to be added to a new mesh
12656             const SMDS_MeshElement* edge;
12657             for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12658             {
12659               if ( iQuad )
12660                 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12661               else
12662                 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12663               if ( edge && avoidSet.insert( edge ).second )
12664                 presentBndElems.push_back( edge );
12665             }
12666           }
12667         }
12668       }
12669     }
12670     else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12671     {
12672       avoidSet.clear(), avoidSet.insert( elem );
12673       elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesElemIterator() ),
12674                         SMDS_MeshElement::iterator() );
12675       elemNodes.push_back( elemNodes[0] );
12676       nodes.resize( 2 + iQuad );
12677       const int nbLinks = elem->NbCornerNodes();
12678       for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12679       {
12680         nodes[0] = elemNodes[iN];
12681         nodes[1] = elemNodes[iN+1+iQuad];
12682         if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12683           continue; // not free link
12684
12685         if ( iQuad ) nodes[2] = elemNodes[iN+1];
12686         if ( const SMDS_MeshElement* edge =
12687              aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12688           presentBndElems.push_back( edge );
12689         else
12690           missingBndElems.push_back( nodes );
12691       }
12692     }
12693
12694     // ---------------------------------
12695     // 2. Add missing boundary elements
12696     // ---------------------------------
12697     if ( targetMesh != myMesh )
12698       // instead of making a map of nodes in this mesh and targetMesh,
12699       // we create nodes with same IDs.
12700       for ( size_t i = 0; i < missingBndElems.size(); ++i )
12701       {
12702         TConnectivity& srcNodes = missingBndElems[i];
12703         tgtNodes.resize( srcNodes.size() );
12704         for ( inode = 0; inode < srcNodes.size(); ++inode )
12705           tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12706         if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( tgtNodes,
12707                                                                    missType,
12708                                                                    /*noMedium=*/false))
12709           continue;
12710         tgtEditor.AddElement( tgtNodes, elemKind.SetPoly( tgtNodes.size()/(iQuad+1) > 4 ));
12711         ++nbAddedBnd;
12712       }
12713     else
12714       for ( size_t i = 0; i < missingBndElems.size(); ++i )
12715       {
12716         TConnectivity& nodes = missingBndElems[ i ];
12717         if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12718                                                                    missType,
12719                                                                    /*noMedium=*/false))
12720           continue;
12721         SMDS_MeshElement* newElem =
12722           tgtEditor.AddElement( nodes, elemKind.SetPoly( nodes.size()/(iQuad+1) > 4 ));
12723         nbAddedBnd += bool( newElem );
12724
12725         // try to set a new element to a shape
12726         if ( myMesh->HasShapeToMesh() )
12727         {
12728           bool ok = true;
12729           set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12730           const size_t nbN = nodes.size() / (iQuad+1 );
12731           for ( inode = 0; inode < nbN && ok; ++inode )
12732           {
12733             pair<int, TopAbs_ShapeEnum> i_stype =
12734               helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12735             if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12736               mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12737           }
12738           if ( ok && mediumShapes.size() > 1 )
12739           {
12740             set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12741             pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12742             for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12743             {
12744               if (( ok = ( stype_i->first != stype_i_0.first )))
12745                 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12746                                         aMesh->IndexToShape( stype_i_0.second ));
12747             }
12748           }
12749           if ( ok && mediumShapes.begin()->first == missShapeType )
12750             aMesh->SetMeshElementOnShape( newElem, mediumShapes.begin()->second );
12751         }
12752       }
12753
12754     // ----------------------------------
12755     // 3. Copy present boundary elements
12756     // ----------------------------------
12757     if ( toCopyExistingBoundary )
12758       for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12759       {
12760         const SMDS_MeshElement* e = presentBndElems[i];
12761         tgtNodes.resize( e->NbNodes() );
12762         for ( inode = 0; inode < tgtNodes.size(); ++inode )
12763           tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12764         presentEditor->AddElement( tgtNodes, elemToCopy.Init( e ));
12765       }
12766     else // store present elements to add them to a group
12767       for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12768       {
12769         presentEditor->myLastCreatedElems.Append( presentBndElems[ i ]);
12770       }
12771
12772   } // loop on given elements
12773
12774   // ---------------------------------------------
12775   // 4. Fill group with boundary elements
12776   // ---------------------------------------------
12777   if ( group )
12778   {
12779     if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12780       for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i )
12781         g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 ));
12782   }
12783   tgtEditor.myLastCreatedElems.Clear();
12784   tgtEditor2.myLastCreatedElems.Clear();
12785
12786   // -----------------------
12787   // 5. Copy given elements
12788   // -----------------------
12789   if ( toCopyElements && targetMesh != myMesh )
12790   {
12791     if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12792     else                  eIt = elemSetIterator( elements );
12793     while (eIt->more())
12794     {
12795       const SMDS_MeshElement* elem = eIt->next();
12796       tgtNodes.resize( elem->NbNodes() );
12797       for ( inode = 0; inode < tgtNodes.size(); ++inode )
12798         tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12799       tgtEditor.AddElement( tgtNodes, elemToCopy.Init( elem ));
12800
12801       tgtEditor.myLastCreatedElems.Clear();
12802     }
12803   }
12804   return nbAddedBnd;
12805 }
12806
12807 //================================================================================
12808 /*!
12809  * \brief Copy node position and set \a to node on the same geometry
12810  */
12811 //================================================================================
12812
12813 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
12814                                      const SMDS_MeshNode* to )
12815 {
12816   if ( !from || !to ) return;
12817
12818   SMDS_PositionPtr pos = from->GetPosition();
12819   if ( !pos || from->getshapeId() < 1 ) return;
12820
12821   switch ( pos->GetTypeOfPosition() )
12822   {
12823   case SMDS_TOP_3DSPACE: break;
12824
12825   case SMDS_TOP_FACE:
12826   {
12827     const SMDS_FacePosition* fPos = static_cast< const SMDS_FacePosition* >( pos );
12828     GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
12829                                 fPos->GetUParameter(), fPos->GetVParameter() );
12830     break;
12831   }
12832   case SMDS_TOP_EDGE:
12833   {
12834     // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
12835     const SMDS_EdgePosition* ePos = static_cast< const SMDS_EdgePosition* >( pos );
12836     GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
12837     break;
12838   }
12839   case SMDS_TOP_VERTEX:
12840   {
12841     GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
12842     break;
12843   }
12844   case SMDS_TOP_UNSPEC:
12845   default:;
12846   }
12847 }